Linux编程学习笔记:多线程编程

一、线程理论基础

    优点:

    1、非常‘节俭’的多任务操作方式,在Linux系统中,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表类维护它的代码段、堆栈段和数据段,只是一种“昂贵”的多任务工作方式

    运行一个进程中的多个线程,他们之间使用相同的地址空间,而且线程间彼此切换所需的时间也远远小于进程间切换所需要的时间,约为进程开销的1/30

    2、线程间方便的通信机制,对不同进程来说它们具有独立的数据空间,要进行数据的传递只能通过进程间通信的方式进行,线程不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其他线程所用,这不仅快捷,而且方便

    3、使用CPU系统更加有效,操作系统会保证当线程数目不大于CPU数目时,不同的线程运行于不同的CPU上

    4、改善程序结构,一个即长又复杂的进程可以考虑分为多个进程,成为几个独立或半独立的运行部分,这样的程序会李雨理解和修改

    Linux系统下的多线程遵循POSIX线程接口,成为pthread。编写Linux下的多线程程序,需要使用头文件pehread.h,连接时需要使用库libptread.h

二、多线程程序设计

    创建线程

        int pthread_create(pthread_t*restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict_arg);

         tidp:线程ID

        restrict_attr:线程属性(通常为空)

        start_rtn:线程要执行的函数

        restrict_arg:start_rtn的参数

 

    编译

        因为pthread的库不是linux系统的库,所以在编译时要加上-lpthread

        gcc filename -lpthread

#include <stdio.h>
#include <pthread.h>

void *myThread1(void)
{
	int i;
	printf("OK\n");
	for(i=0;i<100;i++)
	{
		printf("This is the 1st pthread,created by gyy.\n");
		sleep(1);
	}
}
void *myThread2(void)
{
	int i;
	for(i=0;i<100;i++)
	{
		printf("This is the 2nd pthread,created by gyy.\n");
		sleep(1);
	}
}

int main()
{
	int i=0,ret=0;
	pthread_t id1,id2;
	printf("OK1\n");
	ret=pthread_create(&id1,NULL,(void *)myThread1,NULL);
	if(ret)
	{
		printf("Create pthread1 error!\n");
		return 1;
	}
	ret=pthread_create(&id2,NULL,(void *)myThread2,NULL);
	if(ret)
	{
		printf("Create pthread1 error!\n");
		return 1;
	}
	pthread_join(id1,NULL);
	pthread_join(id2,NULL);
	return 0;	
}

        运行结果

[gyy@localhost thread]$ gcc thread_create.c -lpthread -o thread_create
[gyy@localhost thread]$ ./thread_create
This is the 1st pthread,created by gyy.
This is the 2nd pthread,created by gyy.
This is the 1st pthread,created by gyy.
This is the 2nd pthread,created by gyy.
This is the 1st pthread,created by gyy.
This is the 2nd pthread,created by gyy.
This is the 1st pthread,created by gyy.
This is the 2nd pthread,created by gyy.
This is the 1st pthread,created by gyy.
This is the 2nd pthread,created by gyy.
This is the 1st pthread,created by gyy.

    向线程传递参数例程

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *create(void *arg)
{
	int *num;
	num=(int *)arg;
	printf("create parameter is %d\n",*num);
	return (void *) 0;
}

int main(int argc,char *argv[])
{
	pthread_t tidp;
	int error;
	int test=4;
	int *attr=&test;
	error=pthread_create(&tidp,NULL,create,(void *)attr);
	if(error)
	{
		printf("pthread_create is not created...\n");
		return -1;
	} 
	sleep(1);
	printf("p_thread_create is created...\n");
	return 0;
}

        运行结果

[gyy@localhost thread]$ gcc thread_int.c -lpthread -o thread_int

[gyy@localhost thread]$ ./thread_int

create parameter is 4

p_thread_create is created...

       代码分析:展示如何向线程传递整型参数,先得到参数的地址,然后转为(void *)型,然后传递给thread_create,参数的类型的定死的,必须为指针

    线程间资源共享例程1

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>


int a=1;

void *create(void *arg)
{
	printf("new pthread...\n");
	printf("in new pthread a= %d\n",a);
	a++;
	return (void *) 0;
}

int main(int argc,char *argv[])
{
	pthread_t tidp;
	int error;
	printf("in main 1 a=%d\n",a);
	error=pthread_create(&tidp,NULL,create,NULL);
	if(error)
	{
		printf("pthread_create is not created...\n");
		return -1;
	} 
	sleep(1);
	printf("in main 2 a=%d\n",a);
	return 0;
}

    运行结果

[gyy@localhost thread]$ gcc thread_share.c -lpthread -o thread_share

[gyy@localhost thread]$ ./thread_share

in main 1 a=1

new pthread...

in new pthread a= 1

in main 2 a=2

    代码分析:线程使用了全局变量并将a自增

    线程间资源共享例程2

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

int a=1;

void *create(void *arg)
{
	printf("new pthread...\n");
	printf("in new pthread a= %d\n",a);
	a++;
	return (void *) 0;
}

int main(int argc,char *argv[])
{
	pthread_t tidp;
	int error;
	int a=4;
	printf("in main 1 a=%d\n",a);
	error=pthread_create(&tidp,NULL,create,NULL);
	if(error)
	{
		printf("pthread_create is not created...\n");
		return -1;
	} 
	sleep(1);
	printf("in main 2 a=%d\n",a);
	return 0;
}

    运行结果

[gyy@localhost thread]$ gcc thread_share2.c -lpthread -o thread_share2

[gyy@localhost thread]$ ./thread_share2

in main 1 a=4

new pthread...

in new pthread a= 1

in main 2 a=4

    代码分析:main中的代码使用局部变量,线程中的代码使用全局变量

在线程中应当是优先使用数据段的变量(在堆中的),局部变量存放在栈中

 

    终止线程

        如果任何一个线程中调用exit或_exit,那么整个进程都会终止,线程正常的退出方式有:

        1、线程从启动例程中返回

        2、线程可以被另一个进程终止

        3、线程自己调用pthread_exit函数

        void pthread_exit(void *rval_ptr)

        终止调用的线程

        rval_ptr:线程退出返回值的指针

        线程使用终止例程

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *create(void *arg)
{
	printf("new thread is create...\n");
	return (void *) 8;
}

int main(int argc,char *crgv[])
{
	pthread_t tid;
	int error;
	void *temp;
	
	error=pthread_create(&tid,NULL,create,NULL);
	printf("main thread!\n");
	
	if(error)
	{
		printf("thread is not create...\n");
		return -1;
	}
	error=pthread_join(tid,&temp);

	if(error)
	{
		printf("thread is not exit...\n");
		return -2;
	}
	printf("thread's exit code %d \n",(int)temp);
	return 0;
}

           运行结果

[gyy@localhost thread]$ ./thread_exit

main thread!

new thread is create...

thread's exit code 8

        代码分析:线程从启动程序中返回

        线程调用pthread_exit终止例程

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *create(void *arg)
{
	int ret=10;
	printf("new thread is create...\n");
	pthread_exit((void *)ret);
}

int main(int argc,char *crgv[])
{
	pthread_t tid;
	int error;
	void *temp;
	
	error=pthread_create(&tid,NULL,create,NULL);
	printf("main thread!\n");
	
	if(error)
	{
		printf("thread is not create...\n");
		return -1;
	}
	error=pthread_join(tid,&temp);

	if(error)
	{
		printf("thread is not exit...\n");
		return -2;
	}
	printf("thread's exit code %d \n",(int)temp);
	return 0;
}

        运行结果

[gyy@localhost thread]$ ./thread_exit

main thread!

new thread is create...

thread's exit code 10

        代码分析:线程调用pthread_exit函数退出

 

    线程等待

        int pthread_join(pthread_t tid,void *rval_ptr);

        阻塞调用线程,直到指定的线程终止

        tid:等待推出的线程ID

        rval_ptr:线程退出的返回值的指针

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>

void *thread(void)
{
	int i;
	for(i=0;i<5;i++)
	{
		sleep(2);
		printf("This is in the new thread: %d\n",i);
	}
	return NULL;
}


int main()
{
	pthread_t tid;
	int i;
	int ret=pthread_create(&tid,NULL,thread,NULL);
	
	pthread_join(tid,NULL);
	printf("in the main\n");
	for(i=0;i<5;++i)
	{
		sleep(1);
		printf("This is in the main:%d\n",i);
	}
	return 0;
}
	

        运行结果

[gyy@localhost thread]$ gcc thread_join.c -lpthread -o thread_join

[gyy@localhost thread]$ ./thread_join

This is in the new thread: 0

This is in the new thread: 1

This is in the new thread: 2

This is in the new thread: 3

This is in the new thread: 4

in the main

This is in the main:0

This is in the main:1

This is in the main:2

This is in the main:3

This is in the main:4

        代码分析:调用pthread_join函数后阻塞调用线程main,直到线程thread执行完毕后main线程才继续执行

        注释掉pthread_join后运行结果

[gyy@localhost thread]$ ./thread_join

in the main

This is in the main:0

This is in the new thread: 0

This is in the main:1

This is in the main:2

This is in the new thread: 1

This is in the main:3

This is in the main:4

        代码分析:同时执行,创建线程后线程处于就绪态,在执行到sleep时才会发生调度,当进程退出时它当中的所有线程都退出

        当注释掉main中的sleep运行结果

[gyy@localhost thread]$ ./thread_join

in the main

This is in the main:0

This is in the main:1

This is in the main:2

This is in the main:3

This is in the main:4

        代码分析:不会发生调度

 

    线程标识

        pthread_t pthread_self(void)

        获取调用线程的thread identifier

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *create(void *arg)
{
	printf("New thread...\n");
	printf("This thread's id is %u \n",(unsigned int)pthread_self());
	printf("The process pid is %d\n",getpid());
	return (void *)0;
}

int main()
{
	pthread_t tid;
	int error;
	printf("Main thread is starting...\n");
	error=pthread_create(&tid,NULL,create,NULL);

	if(error)
	{
		printf("thread is not create...\n");
		return -1;
	}
	printf("The main thread's id is %u \n",(unsigned int)pthread_self());
	printf("The main process pid is %d\n",getpid());
	sleep(1);
	return 0;
	
}

        运行结果

[gyy@localhost thread]$ ./thread_id

Main thread is starting...

The main thread's id is 2694051648

The main process pid is 7538

New thread...

This thread's id is 2685671168

The process pid is 7538

    清除

        线程终止有两种情况:正常终止和非正常终止,正常终止就是调用pthread_exit或者return;而非正常终止是线程在其他线程的干预下,或者由于自身运行出错(比如访问非法地址)而退出,这种退出方式是不可预见的

        不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,要确保线程终止时能释放自己的资源

        从pthread_cleanup_push的调用点到pthread_cleanup_pop之间的程序段中的终止动作(包括调用pthread_exit()异常终止,不包括return)都将执行pthread_cleanup_push()所指定的清理函数

        可以使用这俩函数将某段代码保护起来

        void pthread_cleanup_push (void(*rtn)(void *),void *arg)

        将清除函数压入清除栈

        rtn:清除函数

        arg:清除函数的参数

        void pthread_cleanup_pop (int execute)

        将清除函数弹出清除栈

        execute:是否在弹出清除函数的同时执行该函数,非0执行,0不执行

#include <stdio.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>

void clean(void *arg)
{
	printf("cleanup: %s \n",(char *)arg);
	return (void *)0;
}

void *thr_fn1(void *arg)
{
	printf("thread 1 start... \n");
	pthread_cleanup_push((void *)clean,"thread 1 first handler");
	pthread_cleanup_push((void *)clean,"thread 1 second handler");
	printf("thread 1 push complete\n");
	if(arg)
	{
		return ((void *)1);
	}
	pthread_cleanup_pop(0);
	pthread_cleanup_pop(0);
	return (void *)1;
}

void *thr_fn2(void *arg)
{
	printf("thread 2 start... \n");
	pthread_cleanup_push((void *)clean,"thread 2 first handler");
	pthread_cleanup_push((void *)clean,"thread 2 second handler");
	printf("thread 2 push complete\n");
	if(arg)
	{
		pthread_exit((void *)2);
	}
	pthread_cleanup_pop(0);
	pthread_cleanup_pop(0);
	pthread_exit((void *)2);
}

int main(void)
{
	int error;
	pthread_t tid1,tid2;
	void *tret;
	
	error=pthread_create(&tid1,NULL,thr_fn1,(void *)1);
	if(error!=0)
	{
		printf("threat1 create error...\n");
		return -1;
	}
	
	error=pthread_create(&tid2,NULL,thr_fn2,(void *)1);
	if(error!=0)
	{
		printf("threat1 create error...\n");
		return -1;
	}

	error=pthread_join(tid1,&tret);
	if(error)
	{
		printf("thread1 join error...\n");
		return -1;
	}
	printf("thread1 exit code %d\n",(int)tret);
	error=pthread_join(tid2,&tret);
	if(error)
	{
		printf("thread2 join error...\n");
		return -1;
	}
	printf("thread2 exit code %d\n",(int)tret);
}

        运行结果

[gyy@localhost thread]$ ./thread_clean

thread 2 start...

thread 1 start...

thread 1 push complete

thread 2 push complete

cleanup: thread 2 second handler

cleanup: thread 2 first handler

thread1 exit code 1

thread2 exit code 2

        代码分析:thread1是用return终止线程,所以说不会调用clean,thread使用exit终止,所以说会调用clean,而存储方式是栈,所以second handler先被打印(它后被压入,后入先出)

        都注释掉pop前面的终止(即让程序运行到pop)例程

#include <stdio.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>

void clean(void *arg)
{
	printf("cleanup: %s \n",(char *)arg);
	return (void *)0;
}

void *thr_fn1(void *arg)
{
	printf("thread 1 start... \n");
	pthread_cleanup_push((void *)clean,"thread 1 first handler");
	pthread_cleanup_push((void *)clean,"thread 1 second handler");
	printf("thread 1 push complete\n");
//	if(arg)
//	{
//		return ((void *)1);
//	}
	pthread_cleanup_pop(0);
	pthread_cleanup_pop(0);
	return (void *)1;
}

void *thr_fn2(void *arg)
{
	printf("thread 2 start... \n");
	pthread_cleanup_push((void *)clean,"thread 2 first handler");
	pthread_cleanup_push((void *)clean,"thread 2 second handler");
	printf("thread 2 push complete\n");
//	if(arg)
//	{
//		pthread_exit((void *)2);
//	}
	pthread_cleanup_pop(0);
	pthread_cleanup_pop(0);
	pthread_exit((void *)2);
}

int main(void)
{
	int error;
	pthread_t tid1,tid2;
	void *tret;
	
	error=pthread_create(&tid1,NULL,thr_fn1,(void *)1);
	if(error!=0)
	{
		printf("threat1 create error...\n");
		return -1;
	}
	
	error=pthread_create(&tid2,NULL,thr_fn2,(void *)1);
	if(error!=0)
	{
		printf("threat1 create error...\n");
		return -1;
	}

	error=pthread_join(tid1,&tret);
	if(error)
	{
		printf("thread1 join error...\n");
		return -1;
	}
	printf("thread1 exit code %d\n",(int)tret);
	error=pthread_join(tid2,&tret);
	if(error)
	{
		printf("thread2 join error...\n");
		return -1;
	}
	printf("thread2 exit code %d\n",(int)tret);
}

        运行结果

[gyy@localhost thread]$ ./thread_clean2

thread 2 start...

thread 2 push complete

thread 1 start...

thread 1 push complete

thread1 exit code 1

thread2 exit code 2

        代码分析:pop的参数为0,运行到这里弹出clean的时候不会执行

        将pop的参数都改为1例程

#include <stdio.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>

void clean(void *arg)
{
	printf("cleanup: %s \n",(char *)arg);
	return (void *)0;
}

void *thr_fn1(void *arg)
{
	printf("thread 1 start... \n");
	pthread_cleanup_push((void *)clean,"thread 1 first handler");
	pthread_cleanup_push((void *)clean,"thread 1 second handler");
	printf("thread 1 push complete\n");
//	if(arg)
//	{
//		return ((void *)1);
//	}
	pthread_cleanup_pop(1);
	pthread_cleanup_pop(1);
	return (void *)1;
}

void *thr_fn2(void *arg)
{
	printf("thread 2 start... \n");
	pthread_cleanup_push((void *)clean,"thread 2 first handler");
	pthread_cleanup_push((void *)clean,"thread 2 second handler");
	printf("thread 2 push complete\n");
//	if(arg)
//	{
//		pthread_exit((void *)2);
//	}
	pthread_cleanup_pop(1);
	pthread_cleanup_pop(1);
	pthread_exit((void *)2);
}

int main(void)
{
	int error;
	pthread_t tid1,tid2;
	void *tret;
	
	error=pthread_create(&tid1,NULL,thr_fn1,(void *)1);
	if(error!=0)
	{
		printf("threat1 create error...\n");
		return -1;
	}
	
	error=pthread_create(&tid2,NULL,thr_fn2,(void *)1);
	if(error!=0)
	{
		printf("threat1 create error...\n");
		return -1;
	}

	error=pthread_join(tid1,&tret);
	if(error)
	{
		printf("thread1 join error...\n");
		return -1;
	}
	printf("thread1 exit code %d\n",(int)tret);
	error=pthread_join(tid2,&tret);
	if(error)
	{
		printf("thread2 join error...\n");
		return -1;
	}
	printf("thread2 exit code %d\n",(int)tret);
}

        运行结果

[gyy@localhost thread]$ ./thread_clean2

thread 2 start...

thread 2 push complete

cleanup: thread 2 second handler

cleanup: thread 2 first handler

thread 1 start...

thread 1 push complete

cleanup: thread 1 second handler

cleanup: thread 1 first handler

thread1 exit code 1

thread2 exit code 2

        代码分析:pop的参数为1,运行到这里弹出clean并执行

 

猜你喜欢

转载自blog.csdn.net/a568713197/article/details/86751383
今日推荐