linux 下多线程编程 ( C 语言编程).pdf

上传人:asd****56 文档编号:70322307 上传时间:2023-01-19 格式:PDF 页数:36 大小:365.62KB
返回 下载 相关 举报
linux 下多线程编程 ( C 语言编程).pdf_第1页
第1页 / 共36页
linux 下多线程编程 ( C 语言编程).pdf_第2页
第2页 / 共36页
点击查看更多>>
资源描述

《linux 下多线程编程 ( C 语言编程).pdf》由会员分享,可在线阅读,更多相关《linux 下多线程编程 ( C 语言编程).pdf(36页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、linuxlinux 下下 C C 语言多线程编程实例语言多线程编程实例 作者:infobillows 发表日期:2007-01-15 16:34 点击数:31201 学东西,往往实例才是最让人感兴趣的,老是学基础理论,不动手,感觉没有成就感,呵呵。下面先来一个实例。我们通过创建两个线程来实现对一个数的递加。或许这个实例没有实际运用的价值,但是稍微改动一下,我们就可以用到其他地方去拉。下面是我们的代码:/*thread_example.c:c multiple thread programming in linux *author:falcon *E-mail: */#include#incl

2、ude#include#include#define MAX 10 pthread_t thread2;pthread_mutex_t mut;int number=0,i;void*thread1()printf(thread1:Im thread 1n);for(i=0;i MAX;i+)printf(thread1:number=%dn,number);pthread_mutex_lock(&mut);number+;pthread_mutex_unlock(&mut);sleep(2);printf(thread1:主函数在等我完成任务吗?n);pthread_exit(NULL);v

3、oid*thread2()printf(thread2:Im thread 2n);for(i=0;i MAX;i+)printf(thread2:number=%dn,number);pthread_mutex_lock(&mut);number+;pthread_mutex_unlock(&mut);sleep(3);printf(thread2:主函数在等我完成任务吗?n);pthread_exit(NULL);void thread_create(void)int temp;memset(&thread,0,sizeof(thread);/comment1 /*创建线程*/if(tem

4、p=pthread_create(&thread0,NULL,thread1,NULL)!=0)/comment2 printf(线程 1 创建失败!n);else printf(线程 1 被创建n);if(temp=pthread_create(&thread1,NULL,thread2,NULL)!=0)/comment3 printf(线程 2 创建失败);else printf(线程 2 被创建n);void thread_wait(void)/*等待线程结束*/if(thread0!=0)/comment4 pthread_join(thread0,NULL);printf(线程 1

5、 已经结束n);if(thread1!=0)/comment5 pthread_join(thread1,NULL);printf(线程 2 已经结束n);int main()/*用默认属性初始化互斥锁*/pthread_mutex_init(&mut,NULL);printf(我是主函数哦,我正在创建线程,呵呵n);thread_create();printf(我是主函数哦,我正在等待线程完成任务阿,呵呵n);thread_wait();return 0;下面我们先来编译、执行一下 引文:falconfalcon:/program/c/code/ftp$gcc-lpthread-o thre

6、ad_example thread_example.c falconfalcon:/program/c/code/ftp$./thread_example 我是主函数哦,我正在创建线程,呵呵 线程 1 被创建 线程 2 被创建 我是主函数哦,我正在等待线程完成任务阿,呵呵 thread1:Im thread 1 thread1:number=0 thread2:Im thread 2 thread2:number=1 thread1:number=2 thread2:number=3 thread1:number=4 thread2:number=5 thread1:number=6 thre

7、ad1:number=7 thread2:number=8 thread1:number=9 thread2:number=10 thread1:主函数在等我完成任务吗?线程 1 已经结束 thread2:主函数在等我完成任务吗?线程 2 已经结束 实例代码里头的注释应该比较清楚了吧,下面我把网路上介绍上面涉及到的几个函数和变量给引用过来。引文:线程相关操作 一 pthread_t pthread_t 在头文件/usr/include/bits/pthreadtypes.h 中定义:typedef unsigned long int pthread_t;它是一个线程的标识符。二 pthread

8、_create 函数 pthread_create 用来创建一个线程,它的原型为:extern int pthread_create _P(pthread_t*_thread,_const pthread_attr_t*_attr,void*(*_start_routine)(void*),void*_arg);第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的函 数 thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下

9、一节 阐述。当创建线程成功时,函数返回 0,若不为 0 则说明创建线程失败,常见的错误返回代码为 EAGAIN 和 EINVAL。前者表示系统限制创建新的线程,例 如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行 代码。三 pthread_join pthread_exit 函数 pthread_join 用来等待一个线程的结束。函数原型为:extern int pthread_join _P(pthread_t _th,void*_thread_return);第一个参数为被等待的线程标识符,第二

10、个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将 一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的 线程也就结束了;另一种方式是通过函数 pthread_exit 来实现。它的函数原型为:extern void pthread_exit _P(void*_retval)_attribute_(_noreturn_);唯一的参数是函数的返回代码,只要 pthread_join 中的第二个参数 thread_return不是 NULL,这个值将被

11、传递给 thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用 pthread_join 的线 程则返回错误代码 ESRCH。在这一节里,我们编写了一个最简单的线程,并掌握了最常用的三个函数pthread_create,pthread_join 和 pthread_exit。下面,我们来了解线程的一些常用属性以及如何设置这些属性。互斥锁相关 互斥锁用来保证一段时间内只有一个线程在执行一段代码。一 pthread_mutex_init 函 数 pthread_mutex_init 用来生成一个互斥锁。NULL 参数表明使用默认属性。如

12、果需要声明特定属性的互斥锁,须调用函数 pthread_mutexattr_init。函数pthread_mutexattr_setpshared 和函数 pthread_mutexattr_settype 用来设置互斥锁属性。前一个函数设置属性 pshared,它有两个取值,PTHREAD_PROCESS_PRIVATE 和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步,后者用于同步本进程的 不同线程。在上面的例子中,我们使用的是默认属性 PTHREAD_PROCESS_ PRIVATE。后者用来设置互斥锁类型,可选的类型有 PTHREAD_MUTEX_NORMA

13、L、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE 和 PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机制,一般情况下,选用最后一个默认属性。二 pthread_mutex_lock pthread_mutex_unlock pthread_delay_np pthread_mutex_lock 声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock 为止,均被上锁,即同一时间只 能被一个线程调用执行。当一个线程执行到 pthread_mutex_lock 处时,如果该锁此时被另一个线程使

14、用,那此线程被阻塞,即程序将等待到另一 个线程释放此互斥锁。注意:1 需要说明的是,上面的两处 sleep 不光是为了演示的需要,也是为了让线程睡眠一段时间,让线程释放互斥锁,等待另一个线程使用此锁。下面的参考资料 1 里 头说明了该问题。但是在 linux 下好像没有 pthread_delay_np 那个函数(我试了一下,提示没有定义该函数的引用),所以我用了 sleep 来代替,不过参考资料 2 中给出另一种方法,好像是通过pthread_cond_timedwait 来代替,里头给出了一种实现的办法。2 请千万要注意里头的注释 comment1-5,那是我花了几个小时才找出的问题所在。

15、如 果没有 comment1 和 comment4,comment5,将导致在 pthread_join 的时候出现段错误,另外,上面的 comment2 和 comment3 是根源所在,所以千万要记得写全代码。因为上面的线程可能没有创建成功,导致下面不可能等到那个线程结束,而在用 pthread_join 的时候出现段错误(访问了未知的内存区)。另外,在使用 memset 的时候,需要包含 string.h 头文件哦 Linux 下的多线程编程下的多线程编程 作者:姚继锋 2001-08-11 09:05:00 来自:http:/www.china- 1 引言引言 线程(thread)技术

16、早在 60 年代就被提出,但真正应用多线程到操作系统中去,是在80 年代中期,solaris 是这方面的佼佼者。传统的 Unix 也支持线程的概念,但是在一个进程(process)中只允许有一个线程,这样多线程就意味着多进程。现在,多线程技术已经被许多操作系统所支持,包括 Windows/NT,当然,也包括 Linux。为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题。使用多线程的理由之一是和进程相比,它是一种非常节俭的多任务操作方式。我们知道,在 Linux 系统下,启动一个新的进程必须分配给它独立的地址空 间,建立众多的

17、数据表来维护它的代码段、堆栈段和数据段,这是一种昂贵的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址 空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来桓鼋痰目笤际且桓鱿叱炭倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。使用多线程的理由之二 是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不 然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以

18、直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有 的变量不能同时被两个线程所修改,有的子程序中声明为 static 的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地 方。除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:1)提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。2)使多 CPU 系统

19、更加有效。操作系统会保证当线程数不大于 CPU 数目时,不同的线程运行于不同的 CPU 上。3)改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。下面我们先来尝试编写一个简单的多线程程序。2 简单的多线程编程简单的多线程编程 Linux 系统下的多线程遵循 POSIX 线程接口,称为 pthread。编写 Linux 下的多线程程序,需要使用头文件 pthread.h,连接时需要 使用库 libpthread.a。顺便说一下,Linux 下 pthread的实现是通过系统调用 clone()来实现的。clone()是 Linux

20、 所特 有的系统调用,它的使用方式类似 fork,关于 clone()的详细情况,有兴趣的读者可以去查看有关文档说明。下面我们展示一个最简单的多线程程序 example1.c。/*example.c*/#include#include void thread(void)int i;for(i=0;i3;i+)printf(This is a pthread.n);int main(void)pthread_t id;int i,ret;ret=pthread_create(&id,NULL,(void*)thread,NULL);if(ret!=0)printf(Create pthread

21、error!n);exit(1);for(i=0;i3;i+)printf(This is the main process.n);pthread_join(id,NULL);return(0);我们编译此程序:gcc example1.c-lpthread-o example1 运行 example1,我们得到如下结果:This is the main process.This is a pthread.This is the main process.This is the main process.This is a pthread.This is a pthread.再次运行,我们可能

22、得到如下结果:This is a pthread.This is the main process.This is a pthread.This is the main process.This is a pthread.This is the main process.前后两次结果不一样,这是两个线程争夺 CPU 资源的结果。上面的示例中,我们使用到了两个函数,pthread_create 和 pthread_join,并声明了一个 pthread_t 型的变量。pthread_t 在头文件/usr/include/bits/pthreadtypes.h 中定义:typedef unsign

23、ed long int pthread_t;它是一个线程的标识符。函数 pthread_create 用来创建一个线程,它的原型为:extern int pthread_create _P(pthread_t*_thread,_const pthread_attr_t*_attr,void*(*_start_routine)(void*),void*_arg);第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的函 数 thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生

24、成默认属性的线程。对线程属性的设定和修改我们将在下一节 阐述。当创建线程成功时,函数返回 0,若不为 0 则说明创建线程失败,常见的错误返回代码为 EAGAIN 和 EINVAL。前者表示系统限制创建新的线程,例 如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行 代码。函数 pthread_join 用来等待一个线程的结束。函数原型为:extern int pthread_join _P(pthread_t _th,void*_thread_return);第一个参数为被等待的线程标识符,第二个参数

25、为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将 一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的 线程也就结束了;另一种方式是通过函数 pthread_exit 来实现。它的函数原型为:extern void pthread_exit _P(void*_retval)_attribute_(_noreturn_);唯一的参数是函数的返回代码,只要 pthread_join 中的第二个参数 thread_return不是 NULL,这个值将被传递给

26、 thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用 pthread_join 的线 程则返回错误代码 ESRCH。在这一节里,我们编写了一个最简单的线程,并掌握了最常用的三个函数pthread_create,pthread_join 和 pthread_exit。下面,我们来了解线程的一些常用属性以及如何设置这些属性。3 3 修改线程的属性修改线程的属性 在上一节的例子里,我们用 pthread_create 函数创建了一个线程,在这个线程中,我们使用了默认参数,即将该函数的第二个参数设为 NULL。的确,对大多数程序来说,使

27、用默认属性就够了,但我们还是有必要来了解一下线程的有关属性。属性结构为 pthread_attr_t,它同样在头文件/usr/include/pthread.h 中定义,喜欢追根问底的人可以自己去查看。属性 值不能直接设置,须使用相关函数进行操作,初始化的函数为 pthread_attr_init,这个函数必须在 pthread_create 函数之前调 用。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省 1M 的堆栈、与父进程同样级别的优先级。关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。轻进

28、程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制 一个或多个线程。默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程 固定的绑在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为 CPU 时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个 轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。设置线程绑定状态的函数为 pthread_attr_setscope,它有两个参数,第一

29、个是指向属性结构的指针,第二个是绑定类型,它有两个取 值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。下面的代码即创建了一个绑定的线 程。#include pthread_attr_t attr;pthread_t tid;/*初始化属性值,均设为默认值*/pthread_attr_init(&attr);pthread_attr_setscope(&attr,PTHREAD_SCOPE_SYSTEM);pthread_create(&tid,&attr,(void*)my_function,NULL);线程的分离状态决定一个线程

30、以什么样的方式来终止自己。在上面的例子中,我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的 线程结束。只有当 pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线 程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为 pthread_attr_setdetachstate(pthread_attr_t*attr,int detachstate)。第二个参数可选为 PTHREAD_CREATE_DETACHED(分离线

31、程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create 函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用 pthread_create 的 线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait 函数,让这个线程等待一会儿,留出足够的时间让函数 pthread_create 返回。设置一段等待时间,是 在多线程编程里常用的方法。但是注

32、意不要使用诸如 wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。另外一个可能常 用的属性是线程的优先级,它存放在结构 sched_param 中。用函数pthread_attr_getschedparam 和函数 pthread_attr_setschedparam 进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。下面即是一段简单的例子。#include#include pthread_attr_t attr;pthread_t tid;sched_param param;int newprio=20;pthread_attr_init(&attr)

33、;pthread_attr_getschedparam(&attr,¶m);param.sched_priority=newprio;pthread_attr_setschedparam(&attr,¶m);pthread_create(&tid,&attr,(void*)myfunction,myarg);4 4 线程的数据处理线程的数据处理 和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数据段,可以方便的获得、修改数据。但这也给多线程编程带来了许多问题。我 们必须当心有多个不同的进程访问相同的变量。许多函数是不可重入的,即同时不能运行一个函数的多个

34、拷贝(除非使用不同的数据段)。在函数中声明的静态变量 常常带来问题,函数的返回值也会有问题。因为如果返回的是函数内部静态声明的空间的地址,则在一个线程调用该函数得到地址后使用该地址指向的数据时,别的 线程可能调用此函数并修改了这一段数据。在进程中共享的变量必须用关键字 volatile 来定义,这是为了防止编译器在优化时(如 gcc中使用-OX 参 数)改变它们的使用方式。为了保护变量,我们必须使用信号量、互斥等方法来保证我们对变量的正确使用。下面,我们就逐步介绍处理线程数据时的有关知识。4.1 4.1 线程数据线程数据 在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里

35、,还有第三种数据类型:线程数据(TSD:Thread-Specific Data)。它和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见 的。例如我们常见的变量 errno,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在 A 线程里输出的很可能是 B 线程的出错信息。要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线程 里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个

36、线程里,它代表同样的数据内容。和线程数据相关的函数主要有 4 个:创建一个键;为一个键指定线程数据;从一个键读取线程数据;删除键。创建键的函数原型为:extern int pthread_key_create _P(pthread_key_t*_key,void(*_destr_function)(void*);第一个参数为指向一个键值的指针,第二个参数指明了一个 destructor 函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释 放绑定在这个键上的内存块。这个函数常和函数 pthread_once(pthread_once_t*once_control,void(*

37、initroutine)(void)一起使用,为了让这个键只被创建一次。函数 pthread_once 声明一个初始化函数,第一次调用 pthread_once 时它执行这 个函数,以后的调用将被它忽略。在下面的例子中,我们创建一个键,并将它和某个数据相关联。我们要定义一个函数 createWindow,这个函数定义一个图形窗口(数据类型为 Fl_Window*,这是图形界面开发工具 FLTK 中的数据类型)。由于各个线程都会调用这个函数,所以我们使用线程数据。/*声明一个键*/pthread_key_t myWinKey;/*函数 createWindow*/void createWindo

38、w(void)Fl_Window*win;static pthread_once_t once=PTHREAD_ONCE_INIT;/*调用函数 createMyKey,创建键*/pthread_once(&once,createMyKey);/*win 指向一个新建立的窗口*/win=new Fl_Window(0,0,100,100,MyWindow);/*对此窗口作一些可能的设置工作,如大小、位置、名称等*/setWindow(win);/*将窗口指针值绑定在键 myWinKey 上*/pthread_setpecific(myWinKey,win);/*函数 createMyKey,创

39、建一个键,并指定了 destructor*/void createMyKey(void)pthread_keycreate(&myWinKey,freeWinKey);/*函数 freeWinKey,释放空间*/void freeWinKey(Fl_Window*win)delete win;这样,在不同的线程中调用函数 createMyWin,都可以得到在线程内部均可见的窗口变量,这个变量通过函数 pthread_getspecific 得到。在上面的例子中,我们已经使用了函数 pthread_setspecific 来将线程数据和一个键绑定在一 起。这两个函数的原型如下:extern in

40、t pthread_setspecific _P(pthread_key_t _key,_const void*_pointer);extern void*pthread_getspecific _P(pthread_key_t _key);这两个函数的参数意义和使用方法是显而易见的。要注意的是,用pthread_setspecific 为一个键指定新的线程数据时,必须自己释放原有的 线程数据以回收空间。这个过程函数 pthread_key_delete 用来删除一个键,这个键占用的内存将被释放,但同样要注意的是,它只释放键占用的 内存,并不释放该键关联的线程数据所占用的内存资源,而且它也不会

41、触发函数 pthread_key_create 中定义的 destructor 函数。线程 数据的释放必须在释放键之前完成。4.2 4.2 互斥锁互斥锁 互斥锁用来保证一段时间内只有一个线程在执行一段代码。必要性显而易见:假设各个线程向同一个文件顺序写入数据,最后得到的结果一定是灾难性的。我们先看下面一段代码。这是一个读/写程序,它们公用一个缓冲区,并且我们假定一个缓冲区只能保存一条信息。即缓冲区只有两个状态:有信息或没有信息。void reader_function(void);void writer_function(void);char buffer;int buffer_has_ite

42、m=0;pthread_mutex_t mutex;struct timespec delay;void main(void)pthread_t reader;/*定义延迟时间*/delay.tv_sec=2;delay.tv_nec=0;/*用默认属性初始化一个互斥锁对象*/pthread_mutex_init(&mutex,NULL);pthread_create(&reader,pthread_attr_default,(void*)&reader_function),NULL);writer_function();void writer_function(void)while(1)/*

43、锁定互斥锁*/pthread_mutex_lock(&mutex);if(buffer_has_item=0)buffer=make_new_item();buffer_has_item=1;/*打开互斥锁*/pthread_mutex_unlock(&mutex);pthread_delay_np(&delay);void reader_function(void)while(1)pthread_mutex_lock(&mutex);if(buffer_has_item=1)consume_item(buffer);buffer_has_item=0;pthread_mutex_unlock

44、(&mutex);pthread_delay_np(&delay);这里声明了互斥锁变量 mutex,结构 pthread_mutex_t 为不公开的数据类型,其中包含一个系统分配的属性对象。函数 pthread_mutex_init 用来生成一个互斥锁。NULL 参数表明使用默认属性。如果需要声明特定属性的互斥锁,须调用函数 pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数 pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性 pshared,它有两个取 值,PTHREAD_PROCESS_P

45、RIVATE 和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步,后者用于同步本进 程的不同线程。在上面的例子中,我们使用的是默认属性 PTHREAD_PROCESS_ PRIVATE。后者用来设置互斥锁类型,可选的类型有 PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE 和 PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机制,一般情况下,选用最后一个默认属性。pthread_mutex_lock 声明开始用互斥锁上锁,此后的代码直至调用pthrea

46、d_mutex_unlock 为止,均被上锁,即同一时间只 能被一个线程调用执行。当一个线程执行到 pthread_mutex_lock 处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一 个线程释放此互斥锁。在上面的例子中,我们使用了pthread_delay_np 函数,让线程睡眠一段时间,就是为了防止一个线程始终占据此函数。上面的例子非常简单,就不再介绍了,需要提出的是在使用互斥锁的过程中很有可能会出现死锁:两个线程试图同时占用两个资源,并按不同的次序锁定相应的互 斥锁,例如两个线程都需要锁定互斥锁 1 和互斥锁 2,a 线程先锁定互斥锁 1,b 线程先锁定互斥锁 2

47、,这时就出现了死锁。此时我们可以使用函数 pthread_mutex_trylock,它是函数pthread_mutex_lock 的非阻塞版本,当它发现死锁不可避免时,它会返回相应的信 息,程序员可以针对死锁做出相应的处理。另外不同的互斥锁类型对死锁的处理不一样,但最主要的还是要程序员自己在程序设计注意这一点。4.3 4.3 条件变量条件变量 前一节中我们讲述了如何使用互斥锁来实现线程间数据的共享和通信,互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和 等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当

48、条件不满足时,线程往往解开相应的互斥 锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并 重新测试条件是否满足。一般说来,条件变量被用来进行线承间的同步。条件变量的结构为 pthread_cond_t,函数 pthread_cond_init()被用来初始化一个条件变量。它的原型为:extern int pthread_cond_init _P(pthread_cond_t*_cond,_const pthread_condattr_t*_cond_attr);其中 cond 是一个指向结构 pthr

49、ead_cond_t 的指针,cond_attr 是一个指向结构pthread_condattr_t 的指针。结构 pthread_condattr_t 是条件变量的属性结构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用,默认值是 PTHREAD_ PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用。注意初始化条件变量只有未被使用时才能重新初始化或被释放。释放一个条件变量 的函数为 pthread_cond_ destroy(pthread_cond_t cond)。函数 pthread_cond_wait()使线程阻塞在一个条件变量上。它的函数原型为:

50、extern int pthread_cond_wait _P(pthread_cond_t*_cond,pthread_mutex_t*_mutex);线程解开 mutex 指向的锁并被条件变量 cond 阻塞。线程可以被函数pthread_cond_signal 和函数 pthread_cond_broadcast 唤醒,但是要注意的是,条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如一个变量是 否为 0 等等,这一点我们从后面的例子中可以看到。线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待被下 一次唤醒。这个过程一般用w

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 其他杂项

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁