Linux下C语言的多线程编程学习
一、首先,简单了解一下多线程,从耳熟能详的fork()、pthread中理点头绪出来,然后自己写一个简单的来增加一下信心。
1、Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连 接时需要使用库libpthread.a。因此,后面的编译必须在选项中加入 -lpthread 选项,否则提示找不到pthread_create()这些函数。
2、pthread_t 是一个线程的标识符,创建线程用pthread_create(),等待线程结束用pthread_join(),这样,差不多就可以开始写第一个简单的多线程程序了,简单得类似HelloWorld
下面的是我自己写的一个最简单的程序。
#include <stdio.h> #include <pthread.h> void thread() { int i; for (i=0;i<3;i++) { printf("This is thread %d .\r\n",i); sleep(i); } } int main() { pthread_t id; int i,ret; ret = pthread_create(&id,NULL,(void *)thread,NULL); if (ret != 0) { printf("Create thread error!\r\n"); exit(1); } for (i=0;i<3;i++) { printf("This is the main thread %d .\r\n",i); sleep(i); } pthread_join(id,NULL); return(0); }
结果:
This is thread 0 .
This is thread 1 .
This is the main thread 0 .
This is the main thread 1 .
This is thread 2 .
This is the main thread 2 .
########################
#######绑定轻进程#######
########################
关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。 轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。默 认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程固定的"绑"在一个 轻进程之上。被绑定的线程具有较高的响应速度,这是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设 置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。
不同的机器运行结果有可能不同,是由于两个“并行”的线程抢夺处理器资源造成的。而sleep(i)是我自作聪明的想法,仅
仅是为了让结果看上去更加能体现两个线程是“并行”执行的,其实完全可以省去。
线程属性:是否绑定、是否分离、堆栈地址、堆栈大小、优先级
默认的属性:非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。
#include <pthread.h> pthread_attr_t attr; pthread_t tid; /*初始化属性值,均设为默认值*/ pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); /*PTHREAD_SCOPE_SYSTEM(绑定的) PTHREAD_SCOPE_PROCESS(非绑定的)*/ pthread_create(&tid, &attr, (void *) my_function, NULL);
########################
######设置分离状态######
########################
线程的分离状态决定一个线程以什么样的方式来终止自己。线程的默认属性下,即为非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当 pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运 行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为 pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的 线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是 在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。
########################
#######设置优先级#######
########################
另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam 和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。下面即是一段简单的 例子。
#include <pthread.h> #include <sched.h> pthread_attr_t attr; pthread_t tid; sched_param param; int newprio=20; pthread_attr_init(&attr); pthread_attr_getschedparam(&attr, ¶m); param.sched_priority=newprio; pthread_attr_setschedparam(&attr, ¶m); pthread_create(&tid, &attr, (void *)myfunction, myarg);
About Mutex
mutex是一个互斥锁对象,互斥锁是为了防止多线程同时修改某一公共资源,我在下面的程序里把它“锁”在了一个叫buffer[10]的缓冲区 上,模型是2个Reader和2个Writer,Reader要等到叫buffer的书架上有书的时候才可以read,而Writer也必须在书架没有放 满的情况下才可以把新写的书放到书架上。我的程序里书架的大小是1,当然也可以设置书架的大小,不过实现过程大同小异。就不多说了。来看程序:
#include <stdio.h> #include <pthread.h> void reader_function(void); void writer_function(void); char buffer[10]={0}; int buffer_has_item=0; pthread_mutex_t mutex; int main(void) { pthread_t reader; /* 用默认属性初始化一个互斥锁对象*/ pthread_mutex_init (&mutex,NULL); pthread_create(&reader,NULL, (void *)reader_function, NULL); writer_function(); } void writer_function(void) { int i,ti; for(i=1;i<3;) { ti=i; /* 锁定互斥锁*/ pthread_mutex_lock(&mutex); printf("Writer %d Locked Buffer.\r\n",i); if (buffer_has_item==0) { buffer_has_item=1; strcpy(buffer,"Full"); printf("++Writer %d fill the buffer with context \"%s\".\r\n",i,buffer); i++; } /* 打开互斥锁*/ printf("Writer %d Unlocked Buffer.\r\n\n\n",ti); pthread_mutex_unlock(&mutex); sleep(1); } } void reader_function(void) { int i,ti; for(i=1;i<3;) { ti=i; pthread_mutex_lock(&mutex); printf("Reader %d Locked Buffer.\r\n",i); if(buffer_has_item==1) { buffer_has_item=0; strcpy(buffer,"Empty"); printf("--Reader %d clean the buffer with context \"%s\".\r\n",i,buffer); i++; } printf("Reader %d Unlocked Buffer.\r\n\n\n",ti); pthread_mutex_unlock(&mutex); sleep(1); } }
结果:
Reader 1 Locked Buffer.
Reader 1 Unlocked Buffer.
Writer 1 Locked Buffer.
++Writer 1 fill the buffer with context "Full".
Writer 1 Unlocked Buffer.
Writer 2 Locked Buffer.
Writer 2 Unlocked Buffer.
Reader 1 Locked Buffer.
--Reader 1 clean the buffer with context "Empty".
Reader 1 Unlocked Buffer.
Writer 2 Locked Buffer.
++Writer 2 fill the buffer with context "Full".
Writer 2 Unlocked Buffer.
Reader 2 Locked Buffer.
--Reader 2 clean the buffer with context "Empty".
Reader 2 Unlocked Buffer.
注意,这次用到的sleep()不再是为了让结果好看, 而是为了防止一个线程始终占用资源,很多网上的教程使用的是pthread_delay_np(&delay);这个语句,不过这个似乎只能在 solaris系统上用,linux还是用sleep()和usleep()好了,有高人说可能会使该线程所在的进程都sleep,我使用下来似乎没有发 现。而且似乎linux下本来就是一个进程里只有一个线程,忘记哪个手册上说的,也许是以前的版本,不追究了,能用就行。
还 有就是在reader()和writer()里,我用了很奇怪的 i 和 ti 两个变量来对reader和writer编号,主要目的是为了保证每个Reader和Writer都能完成自己的使命,如果按照常规写法,使用for(i =1;i<3;i++)这样控制循环会使有的Reader或者Writer不能取到或者放上书。似乎说得自己都迷糊了,举例说,Reader1去取 书,恰巧这时候书架上是空的,为了保证Reader1能取到书,我就让Reader们排队,直到Reader1取到以后才轮到Reader2,对于 Writer们也实行排队.
转自:http://hi.baidu.com/skyeye_zeus/blog/item/35fdc8a7d93de79dd143588b.html