??xml version="1.0" encoding="utf-8" standalone="yes"?>久久亚洲日韩精品一区二区三区,亚洲国产成人久久综合野外,久久亚洲国产中v天仙wwwhttp://www.shnenglu.com/yehao/category/16706.htmlzh-cnSun, 24 Apr 2011 15:14:55 GMTSun, 24 Apr 2011 15:14:55 GMT60Linux下的多线E编E?/title><link>http://www.shnenglu.com/yehao/articles/144908.html</link><dc:creator>厚积薄发</dc:creator><author>厚积薄发</author><pubDate>Sun, 24 Apr 2011 10:47:00 GMT</pubDate><guid>http://www.shnenglu.com/yehao/articles/144908.html</guid><wfw:comment>http://www.shnenglu.com/yehao/comments/144908.html</wfw:comment><comments>http://www.shnenglu.com/yehao/articles/144908.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/yehao/comments/commentRss/144908.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/yehao/services/trackbacks/144908.html</trackback:ping><description><![CDATA[<font size=3>转自<a >http://fanqiang.chinaunix.net/a4/b8/20010811/0905001105.html</a><br></font><br>1 引言<br>  U程QthreadQ技术早?0q代p提出Q但真正应用多线E到操作pȝ中去Q是?0q代中期Qsolaris是这斚w的佼D。传l的Unix也支持线E的概念Q但是在一个进E(processQ中只允许有一个线E,q样多线E就意味着多进E。现在,多线E技术已l被许多操作pȝ所支持Q包括Windows/NTQ当Ӟ也包括Linux?br>  Z么有了进E的概念后,q要再引入线E呢Q用多U程到底有哪些好处?什么的pȝ应该选用多线E?我们首先必须回答q些问题?br>  使用多线E的理由之一是和q程相比Q它是一U非?节P"的多d操作方式。我们知道,在Linuxpȝ下,启动一个新的进E必d配给它独立的地址I间Q徏立众多的数据表来l护它的代码Dc堆栈段和数据段Q这是一U?昂贵"的多d工作方式。而运行于一个进E中的多个线E,它们彼此之间使用相同的地址I间Q共享大部分数据Q启动一个线E所p的空间远q小于启动一个进E所p的空_而且Q线E间彼此切换所需的时间也q远于q程间切换所需要的旉。据l计Qȝ说来Q一个进E的开销大约是一个线E开销?0倍左叻I当然Q在具体的系l上Q这个数据可能会有较大的区别?br>  使用多线E的理由之二是线E间方便的通信机制。对不同q程来说Q它们具有独立的数据I间Q要q行数据的传递只能通过通信的方式进行,q种方式不仅ҎQ而且很不方便。线E则不然Q由于同一q程下的U程之间׃n数据I间Q所以一个线E的数据可以直接为其它线E所用,q不仅快P而且方便。当Ӟ数据的共享也带来其他一些问题,有的变量不能同时被两个线E所修改Q有的子E序中声明ؓstatic的数据更有可能给多线E程序带来灾难性的打击Q这些正是编写多U程E序时最需要注意的地方?br>  除了以上所说的优点外,不和q程比较Q多U程E序作ؓ一U多d、ƈ发的工作方式Q当然有以下的优点:<br>  1) 提高应用E序响应。这对图形界面的E序其有意义,当一个操作耗时很长Ӟ整个pȝ都会{待q个操作Q此时程序不会响应键盘、鼠标、菜单的操作Q而用多U程技术,耗时长的操作Qtime consumingQ置于一个新的线E,可以避免q种尬的情c?br>  2) 使多CPUpȝ更加有效。操作系l会保证当线E数不大于CPU数目Ӟ不同的线E运行于不同的CPU上?br>  3) 改善E序l构。一个既长又复杂的进E可以考虑分ؓ多个U程Q成为几个独立或半独立的q行部分Q这LE序会利于理解和修改?br>  下面我们先来试~写一个简单的多线E程序?br><br>2 单的多线E编E?br>  Linuxpȝ下的多线E遵循POSIXU程接口Q称为pthread。编写Linux下的多线E程序,需要用头文gpthread.hQ连接时需要用库libpthread.a。顺便说一下,Linux下pthread的实现是通过pȝ调用cloneQ)来实现的。cloneQ)是Linux所Ҏ的系l调用,它的使用方式cMforkQ关于cloneQ)的详l情况,有兴的读者可以去查看有关文档说明。下面我们展CZ个最单的多线E程序example1.c? <p style="COLOR: #000000"></p> <p style="COLOR: #000000">/* example.c*/<br>#include <stdio.h><br>#include <pthread.h><br>void thread(void)<br>{<br>int i;<br>for(i=0;i<3;i++)<br>printf("This is a pthread.\n");<br>}</p> <p style="COLOR: #000000">int main(void)<br>{<br>pthread_t id;<br>int i,ret;<br>ret=pthread_create(&id,NULL,(void *) thread,NULL);<br>if(ret!=0){<br>printf ("Create pthread error!\n");<br>exit (1);<br>}<br>for(i=0;i<3;i++)<br>printf("This is the main process.\n");<br>pthread_join(id,NULL);<br>return (0);<br>}</p> <p style="COLOR: #000000">我们~译此程序:<br>gcc example1.c -lpthread -o example1<br>q行example1Q我们得到如下结果:<br>This is the main process.<br>This is a pthread.<br>This is the main process.<br>This is the main process.<br>This is a pthread.<br>This is a pthread.<br>再次q行Q我们可能得到如下结果:<br>This is a pthread.<br>This is the main process.<br>This is a pthread.<br>This is the main process.<br>This is a pthread.<br>This is the main process.<br><br>  前后两次l果不一Pq是两个U程争夺CPU资源的结果。上面的CZ中,我们使用C两个函数Q  pthread_create和pthread_joinQƈ声明了一个pthread_t型的变量?br>  pthread_t在头文g/usr/include/bits/pthreadtypes.h中定义:<br>  typedef unsigned long int pthread_t;<br>  它是一个线E的标识W。函数pthread_create用来创徏一个线E,它的原型为:<br>  extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,<br>  void *(*__start_routine) (void *), void *__arg));<br>  W一个参Cؓ指向U程标识W的指针Q第二个参数用来讄U程属性,W三个参数是U程q行函数的v始地址Q最后一个参数是q行函数的参数。这里,我们的函数thread不需要参敎ͼ所以最后一个参数设为空指针。第二个参数我们也设为空指针Q这样将生成默认属性的U程。对U程属性的讑֮和修Ҏ们将在下一节阐q。当创徏U程成功Ӟ函数q回0Q若不ؓ0则说明创建线E失败,常见的错误返回代码ؓEAGAIN和EINVAL。前者表C系l限制创建新的线E,例如U程数目q多了;后者表C第二个参数代表的线E属性值非法。创建线E成功后Q新创徏的线E则q行参数三和参数四确定的函数Q原来的U程则l运行下一行代码?br>  函数pthread_join用来{待一个线E的l束。函数原型ؓQ?br>  extern int pthread_join __P ((pthread_t __th, void **__thread_return));<br>  W一个参Cؓ被等待的U程标识W,W二个参Cؓ一个用户定义的指针Q它可以用来存储被等待线E的q回倹{这个函数是一个线E阻塞的函数Q调用它的函数将一直等待到被等待的U程l束为止Q当函数q回Ӟ被等待线E的资源被收回。一个线E的l束有两U途径Q一U是象我们上面的例子一P函数l束了,调用它的U程也就l束了;另一U方式是通过函数pthread_exit来实现。它的函数原型ؓQ?br>  extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));<br>  唯一的参数是函数的返回代码,只要pthread_join中的W二个参数thread_return不是NULLQ这个值将被传递给thread_return。最后要说明的是Q一个线E不能被多个U程{待Q否则第一个接收到信号的线E成功返回,其余调用pthread_join的线E则q回错误代码ESRCH?br>  在这一节里Q我们编写了一个最单的U程Qƈ掌握了最常用的三个函数pthread_createQpthread_join和pthread_exit。下面,我们来了解线E的一些常用属性以及如何设|这些属性?/p> <p style="COLOR: #000000">3 修改U程的属?br>  在上一节的例子里,我们用pthread_create函数创徏了一个线E,在这个线E中Q我们用了默认参数Q即该函数的第二个参数设ؓNULL。的,对大多数E序来说Q用默认属性就够了Q但我们q是有必要来了解一下线E的有关属性?br>  属性结构ؓpthread_attr_tQ它同样在头文g/usr/include/pthread.h中定义,喜欢q根问底的h可以自己L看。属性g能直接设|,M用相兛_数进行操作,初始化的函数为pthread_attr_initQ这个函数必dpthread_create函数之前调用。属性对象主要包括是否绑定、是否分R堆栈地址、堆栈大、优先。默认的属性ؓ非绑定、非分离、缺?M的堆栈、与父进E同L别的优先U?br>  关于U程的绑定,牉|到另外一个概念:轻进E(LWPQLight Weight ProcessQ。轻q程可以理解为内核线E,它位于用户层和系l层之间。系l对U程资源的分配、对U程的控制是通过轻进E来实现的,一个轻q程可以控制一个或多个U程。默认状况下Q启动多轻q程、哪些轻q程来控制哪些线E是ql来控制的,q种状况即称为非l定的。绑定状况下Q则֐思义Q即某个U程固定?l?在一个轻q程之上。被l定的线E具有较高的响应速度Q这是因为CPU旉片的调度是面向轻q程的,l定的线E可以保证在需要的时候它L一个轻q程可用。通过讄被绑定的轻进E的优先U和调度U可以得绑定的U程满诸如实时反应之类的要求?br>  讄U程l定状态的函数为pthread_attr_setscopeQ它有两个参敎ͼW一个是指向属性结构的指针Q第二个是绑定类型,它有两个取|PTHREAD_SCOPE_SYSTEMQ绑定的Q和PTHREAD_SCOPE_PROCESSQ非l定的)。下面的代码卛_Z一个绑定的U程?br>#include <pthread.h><br>pthread_attr_t attr;<br>pthread_t tid;</p> <p style="COLOR: #000000">/*初始化属性|均设为默认?/<br>pthread_attr_init(&attr);<br>pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);</p> <p style="COLOR: #000000">pthread_create(&tid, &attr, (void *) my_function, NULL);</p> <p style="COLOR: #000000">  U程的分ȝ态决定一个线E以什么样的方式来l止自己。在上面的例子中Q我们采用了U程的默认属性,即ؓ非分ȝ态,q种情况下,原有的线E等待创建的U程l束。只有当pthread_joinQ)函数q回Ӟ创徏的线E才终止,才能释放自己占用的系l资源。而分ȝE不是这样子的,它没有被其他的线E所{待Q自p行结束了Q线E也q止了Q马上释攄l资源。程序员应该Ҏ自己的需要,选择适当的分ȝ态。设|线E分ȝ态的函数为pthread_attr_setdetachstateQpthread_attr_t *attr, int detachstateQ。第二个参数可选ؓPTHREAD_CREATE_DETACHEDQ分ȝE)?PTHREAD _CREATE_JOINABLEQ非分离U程Q。这里要注意的一ҎQ如果设|一个线Eؓ分离U程Q而这个线E运行又非常快,它很可能在pthread_create函数q回之前q止了Q它l止以后可能将U程号和pȝ资源UMl其他的U程使用Q这栯用pthread_create的线E就得到了错误的U程受要避免q种情况可以采取一定的同步措施Q最单的Ҏ之一是可以在被创建的U程里调用pthread_cond_timewait函数Q让q个U程{待一会儿Q留够的旉让函数pthread_createq回。设|一D늭待时_是在多线E编E里常用的方法。但是注意不要用诸如waitQ)之类的函敎ͼ它们是整个q程睡眠Qƈ不能解决U程同步的问题?br>  另外一个可能常用的属性是U程的优先Q它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparamq行存放Q一般说来,我们L先取优先U,对取得的g改后再存攑֛厅R下面即是一D늮单的例子?br>#include <pthread.h><br>#include <sched.h><br>pthread_attr_t attr;<br>pthread_t tid;<br>sched_param param;<br>int newprio=20;</p> <p style="COLOR: #000000">pthread_attr_init(&attr);<br>pthread_attr_getschedparam(&attr, &param);<br>param.sched_priority=newprio;<br>pthread_attr_setschedparam(&attr, &param);<br>pthread_create(&tid, &attr, (void *)myfunction, myarg);<br>  <br>4 U程的数据处?br>  和进E相比,U程的最大优点之一是数据的׃n性,各个q程׃n父进E处沿袭的数据段Q可以方便的获得、修Ҏ据。但q也l多U程~程带来了许多问题。我们必d心有多个不同的进E访问相同的变量。许多函数是不可重入的,卛_时不能运行一个函数的多个拯Q除非用不同的数据D)。在函数中声明的静态变量常常带来问题,函数的返回g会有问题。因为如果返回的是函数内部静态声明的I间的地址Q则在一个线E调用该函数得到地址后用该地址指向的数据时Q别的线E可能调用此函数q修改了q一D|据。在q程中共享的变量必须用关键字volatile来定义,q是Z防止~译器在优化Ӟ如gcc中?OX参数Q改变它们的使用方式。ؓ了保护变量,我们必须使用信号量、互斥等Ҏ来保证我们对变量的正用。下面,我们逐步介绍处理U程数据时的有关知识?/p> <p style="COLOR: #000000">4.1 U程数据<br>  在单U程的程序里Q有两种基本的数据:全局变量和局部变量。但在多U程E序里,q有W三U数据类型:U程数据QTSD: Thread-Specific DataQ。它和全局变量很象Q在U程内部Q各个函数可以象使用全局变量一栯用它Q但它对U程外部的其它线E是不可见的。这U数据的必要性是显而易见的。例如我们常见的变量errnoQ它q回标准的出错信息。它昄不能是一个局部变量,几乎每个函数都应该可以调用它Q但它又不能是一个全局变量Q否则在AU程里输出的很可能是BU程的出错信息。要实现诸如此类的变量,我们必M用线E数据。我们ؓ每个U程数据创徏一个键Q它和这个键相关联,在各个线E里Q都使用q个键来指代U程数据Q但在不同的U程里,q个键代表的数据是不同的Q在同一个线E里Q它代表同样的数据内宏V?br>  和线E数据相关的函数主要?个:创徏一个键Qؓ一个键指定U程数据Q从一个键dU程数据Q删除键?br>  创徏键的函数原型为:<br>  extern int pthread_key_create __P ((pthread_key_t *__key,<br>  void (*__destr_function) (void *)));<br>  W一个参Cؓ指向一个键值的指针Q第二个参数指明了一个destructor函数Q如果这个参C为空Q那么当每个U程l束Ӟpȝ调用这个函数来释放l定在这个键上的内存块。这个函数常和函数pthread_once ((pthread_once_t*once_control, void (*initroutine) (void)))一起用,Z让这个键只被创徏一ơ。函数pthread_once声明一个初始化函数Q第一ơ调用pthread_once时它执行q个函数Q以后的调用被它忽略?br><br>  在下面的例子中,我们创徏一个键Qƈ它和某个数据相兌。我们要定义一个函数createWindowQ这个函数定义一个图形窗口(数据cd为Fl_Window *Q这是图形界面开发工具FLTK中的数据cdQ。由于各个线E都会调用这个函敎ͼ所以我们用线E数据?br>/* 声明一个键*/<br>pthread_key_t myWinKey;<br>/* 函数 createWindow */<br>void createWindow ( void ) {<br>Fl_Window * win;<br>static pthread_once_t once= PTHREAD_ONCE_INIT;<br>/* 调用函数createMyKeyQ创建键*/<br>pthread_once ( & once, createMyKey) ;<br>/*win指向一个新建立的窗?/<br>win=new Fl_Window( 0, 0, 100, 100, "MyWindow");<br>/* ҎH口作一些可能的讄工作Q如大小、位|、名U等*/<br>setWindow(win);<br>/* 窗口指针值绑定在键myWinKey?/<br>pthread_setpecific ( myWinKey, win);<br>}</p> <p style="COLOR: #000000">/* 函数 createMyKeyQ创Z个键Qƈ指定了destructor */<br>void createMyKey ( void ) {<br>pthread_keycreate(&myWinKey, freeWinKey);<br>}</p> <p style="COLOR: #000000">/* 函数 freeWinKeyQ释攄?/<br>void freeWinKey ( Fl_Window * win){<br>delete win;<br>}</p> <p style="COLOR: #000000">  q样Q在不同的线E中调用函数createMyWinQ都可以得到在线E内部均可见的窗口变量,q个变量通过函数pthread_getspecific得到。在上面的例子中Q我们已l用了函数pthread_setspecific来将U程数据和一个键l定在一赗这两个函数的原型如下:<br>  extern int pthread_setspecific __P ((pthread_key_t __key,__const void *__pointer));<br>  extern void *pthread_getspecific __P ((pthread_key_t __key));<br>  q两个函数的参数意义和用方法是显而易见的。要注意的是Q用pthread_setspecificZ个键指定新的U程数据Ӟ必须自己释放原有的线E数据以回收I间。这个过E函数pthread_key_delete用来删除一个键Q这个键占用的内存将被释放,但同栯注意的是Q它只释N占用的内存,q不释放该键兌的线E数据所占用的内存资源,而且它也不会触发函数pthread_key_create中定义的destructor函数。线E数据的释放必须在释N之前完成?/p> <p style="COLOR: #000000">4.2 互斥?br>  互斥锁用来保证一D|间内只有一个线E在执行一D代码。必要性显而易见:假设各个U程向同一个文仉序写入数据,最后得到的l果一定是N性的?br>  我们先看下面一D代码。这是一个读/写程序,它们公用一个缓冲区Qƈ且我们假定一个缓冲区只能保存一条信息。即~冲区只有两个状态:有信息或没有信息?/p> <p style="COLOR: #000000">void reader_function ( void );<br>void writer_function ( void ); </p> <p style="COLOR: #000000">char buffer;<br>int buffer_has_item=0;<br>pthread_mutex_t mutex;<br>struct timespec delay;<br>void main ( void ){<br>pthread_t reader;<br>/* 定义延迟旉*/<br>delay.tv_sec = 2;<br>delay.tv_nec = 0;<br>/* 用默认属性初始化一个互斥锁对象*/<br>pthread_mutex_init (&mutex,NULL);<br>pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL);<br>writer_function( );<br>}</p> <p style="COLOR: #000000">void writer_function (void){<br>while(1){<br>/* 锁定互斥?/<br>pthread_mutex_lock (&mutex);<br>if (buffer_has_item==0){<br>buffer=make_new_item( );<br>buffer_has_item=1;<br>}<br>/* 打开互斥?/<br>pthread_mutex_unlock(&mutex);<br>pthread_delay_np(&delay);<br>}<br>}</p> <p style="COLOR: #000000">void reader_function(void){<br>while(1){<br>pthread_mutex_lock(&mutex);<br>if(buffer_has_item==1){<br>consume_item(buffer);<br>buffer_has_item=0;<br>}<br>pthread_mutex_unlock(&mutex);<br>pthread_delay_np(&delay);<br>}<br>}<br>  q里声明了互斥锁变量mutexQ结构pthread_mutex_tZ公开的数据类型,其中包含一个系l分配的属性对象。函数pthread_mutex_init用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,调用函数pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数pthread_mutexattr_settype用来讄互斥锁属性。前一个函数设|属性psharedQ它有两个取|PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用来不同进E中的线E同步,后者用于同步本q程的不同线E。在上面的例子中Q我们用的是默认属性PTHREAD_PROCESS_ PRIVATE。后者用来设|互斥锁cdQ可选的cd有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机Ӟ一般情况下Q选用最后一个默认属性?br>  pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直臌用pthread_mutex_unlock为止Q均被上锁,卛_一旉只能被一个线E调用执行。当一个线E执行到pthread_mutex_lock处时Q如果该锁此时被另一个线E用,那此U程被阻塞,即程序将{待到另一个线E释放此互斥锁。在上面的例子中Q我们用了pthread_delay_np函数Q让U程睡眠一D|_是Z防止一个线E始l占据此函数?br>  上面的例子非常简单,׃再介l了Q需要提出的是在使用互斥锁的q程中很有可能会出现死锁Q两个线E试囑֐时占用两个资源,q按不同的次序锁定相应的互斥锁,例如两个U程都需要锁定互斥锁1和互斥锁2QaU程先锁定互斥锁1QbU程先锁定互斥锁2Q这时就出现了死锁。此时我们可以用函数pthread_mutex_trylockQ它是函数pthread_mutex_lock的非d版本Q当它发现死锁不可避免时Q它会返回相应的信息Q程序员可以针对死锁做出相应的处理。另外不同的互斥锁类型对死锁的处理不一P但最主要的还是要E序员自己在E序设计注意q一炏V?/p> <p style="COLOR: #000000">4.3 条g变量<br>  前一节中我们讲述了如何用互斥锁来实现线E间数据的共享和通信Q互斥锁一个明昄~点是它只有两种状态:锁定和非锁定。而条件变量通过允许U程d和等待另一个线E发送信LҎ弥补了互斥锁的不I它常和互斥锁一起用。用时Q条件变量被用来d一个线E,当条件不满ӞU程往往解开相应的互斥锁q等待条件发生变化。一旦其它的某个U程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的U程。这些线E将重新锁定互斥锁ƈ重新试条g是否满。一般说来,条g变量被用来进行线扉K的同步?br>  条g变量的结构ؓpthread_cond_tQ函数pthread_cond_initQ)被用来初始化一个条件变量。它的原型ؓQ?br>  extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));<br>  其中cond是一个指向结构pthread_cond_t的指针,cond_attr是一个指向结构pthread_condattr_t的指针。结构pthread_condattr_t是条件变量的属性结构,和互斥锁一h们可以用它来讄条g变量是进E内可用q是q程间可用,默认值是PTHREAD_ PROCESS_PRIVATEQ即此条件变量被同一q程内的各个U程使用。注意初始化条g变量只有未被使用时才能重新初始化或被释放。释放一个条件变量的函数为pthread_cond_ destroyQpthread_cond_t condQ。 <br>  函数pthread_cond_waitQ)使线E阻塞在一个条件变量上。它的函数原型ؓQ?br>  extern int pthread_cond_wait __P ((pthread_cond_t *__cond,<br>  pthread_mutex_t *__mutex));<br>  U程解开mutex指向的锁q被条g变量condd。线E可以被函数pthread_cond_signal和函数pthread_cond_broadcast唤醒Q但是要注意的是Q条件变量只是vd和唤醒线E的作用Q具体的判断条gq需用户l出Q例如一个变量是否ؓ0{等Q这一Ҏ们从后面的例子中可以看到。线E被唤醒后,它将重新查判断条件是否满I如果q不满Q一般说来线E应该仍d在这里,被等待被下一ơ唤醒。这个过E一般用while语句实现?br>  另一个用来阻塞线E的函数是pthread_cond_timedwaitQ)Q它的原型ؓQ?br>  extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,<br>  pthread_mutex_t *__mutex, __const struct timespec *__abstime));<br>  它比函数pthread_cond_waitQ)多了一个时间参敎ͼl历abstimeD|间后Q即使条件变量不满Q阻塞也被解除?br>  函数pthread_cond_signalQ)的原型ؓQ?br>  extern int pthread_cond_signal __P ((pthread_cond_t *__cond));<br>  它用来释放被d在条件变量cond上的一个线E。多个线E阻塞在此条件变量上Ӟ哪一个线E被唤醒是由U程的调度策略所军_的。要注意的是Q必ȝ保护条g变量的互斥锁来保护这个函敎ͼ否则条g满信号又可能在试条g和调用pthread_cond_wait函数之间被发出,从而造成无限制的{待。下面是使用函数pthread_cond_waitQ)和函数pthread_cond_signalQ)的一个简单的例子?/p> <p style="COLOR: #000000">pthread_mutex_t count_lock;<br>pthread_cond_t count_nonzero;<br>unsigned count;<br>decrement_count () {<br>pthread_mutex_lock (&count_lock);<br>while(count==0) <br>pthread_cond_wait( &count_nonzero, &count_lock);<br>count=count -1;<br>pthread_mutex_unlock (&count_lock);<br>}<br><br>increment_count(){<br>pthread_mutex_lock(&count_lock);<br>if(count==0)<br>pthread_cond_signal(&count_nonzero);<br>count=count+1;<br>pthread_mutex_unlock(&count_lock);<br>}<br>  countgؓ0Ӟdecrement函数在pthread_cond_wait处被dQƈ打开互斥锁count_lock。此Ӟ当调用到函数increment_countӞpthread_cond_signalQ)函数改变条g变量Q告知decrement_countQ)停止d。读者可以试着让两个线E分别运行这两个函数Q看看会出现什么样的结果?br>  函数pthread_cond_broadcastQpthread_cond_t *condQ用来唤醒所有被d在条件变量cond上的U程。这些线E被唤醒后将再次竞争相应的互斥锁Q所以必d心用这个函数?/p> <p style="COLOR: #000000">4.4 信号?br>  信号量本质上是一个非负的整数计数器,它被用来控制对公p源的讉K。当公共资源增加Ӟ调用函数sem_postQ)增加信号量。只有当信号量值大于0Ӟ才能使用公共资源Q用后Q函数sem_waitQ)减少信号量。函数sem_trywaitQ)和函数pthread_ mutex_trylockQ)起同L作用Q它是函数sem_waitQ)的非d版本。下面我们逐个介绍和信号量有关的一些函敎ͼ它们都在头文?usr/include/semaphore.h中定义?br>  信号量的数据cd为结构sem_tQ它本质上是一个长整型的数。函数sem_initQ)用来初始化一个信号量。它的原型ؓQ?br>  extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));<br>  sem为指向信号量l构的一个指针;pshared不ؓQ时此信号量在进E间׃nQ否则只能ؓ当前q程的所有线E共享;valuel出了信号量的初始倹{?br>  函数sem_post( sem_t *sem )用来增加信号量的倹{当有线E阻塞在q个信号量上Ӟ调用q个函数会其中的一个线E不在阻塞,选择机制同样是由U程的调度策略决定的?br>  函数sem_wait( sem_t *sem )被用来阻塞当前线E直C号量sem的值大?Q解除阻塞后sem的值减一Q表明公p源经使用后减。函数sem_trywait ( sem_t *sem )是函数sem_waitQ)的非d版本Q它直接信号量sem的值减一?br>  函数sem_destroy(sem_t *sem)用来释放信号量sem?br>  下面我们来看一个用信号量的例子。在q个例子中,一共有4个线E,其中两个U程负责从文件读取数据到公共的缓冲区Q另两个U程从缓冲区d数据作不同的处理Q加和乘q算Q?br>/* File sem.c */<br>#include <stdio.h><br>#include <pthread.h><br>#include <semaphore.h><br>#define MAXSTACK 100<br>int stack[MAXSTACK][2];<br>int size=0;<br>sem_t sem;<br>/* 从文?.datd数据Q每Mơ,信号量加一*/<br>void ReadData1(void){<br>FILE *fp=fopen("1.dat","r");<br>while(!feof(fp)){<br>fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);<br>sem_post(&sem);<br>++size;<br>}<br>fclose(fp);<br>}<br>/*从文?.datd数据*/<br>void ReadData2(void){<br>FILE *fp=fopen("2.dat","r");<br>while(!feof(fp)){<br>fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);<br>sem_post(&sem);<br>++size;<br>}<br>fclose(fp);<br>}<br>/*d{待~冲区有数据Q读取数据后Q释攄_l箋{待*/<br>void HandleData1(void){<br>while(1){<br>sem_wait(&sem);<br>printf("Plus:%d+%d=%d\n",stack[size][0],stack[size][1],<br>stack[size][0]+stack[size][1]);<br>--size;<br>}<br>}</p> <p style="COLOR: #000000">void HandleData2(void){<br>while(1){<br>sem_wait(&sem);<br>printf("Multiply:%d*%d=%d\n",stack[size][0],stack[size][1],<br>stack[size][0]*stack[size][1]);<br>--size;<br>}<br>}<br>int main(void){<br>pthread_t t1,t2,t3,t4;<br>sem_init(&sem,0,0);<br>pthread_create(&t1,NULL,(void *)HandleData1,NULL);<br>pthread_create(&t2,NULL,(void *)HandleData2,NULL);<br>pthread_create(&t3,NULL,(void *)ReadData1,NULL);<br>pthread_create(&t4,NULL,(void *)ReadData2,NULL);<br>/* 防止E序q早退出,让它在此无限期等?/<br>pthread_join(t1,NULL);<br>}</p> <p style="COLOR: #000000">  在Linux下,我们用命令gcc -lpthread sem.c -o sem生成可执行文件sem?我们事先~辑好数据文?.dat?.datQ假讑֮们的内容分别? 2 3 4 5 6 7 8 9 10?-1 -2 -3 -4 -5 -6 -7 -8 -9 -10 Q我们运行semQ得到如下的l果Q?br>Multiply:-1*-2=2<br>Plus:-1+-2=-3<br>Multiply:9*10=90<br>Plus:-9+-10=-19<br>Multiply:-7*-8=56<br>Plus:-5+-6=-11<br>Multiply:-3*-4=12<br>Plus:9+10=19<br>Plus:7+8=15<br>Plus:5+6=11<br><br>  从中我们可以看出各个U程间的竞争关系。而数值ƈ未按我们原先的顺序显C出来这是由于sizeq个数D各个U程L修改的缘故。这也往往是多U程~程要注意的问题?/p> <p style="COLOR: #000000">5 结<br>  多线E编E是一个很有意思也很有用的技术,使用多线E技术的|络蚂蚁是目前最常用的下载工具之一Q用多U程技术的grep比单U程的grep要快上几倍,cM的例子还有很多。希望大家能用多U程技术写出高效实用的好程序来?/p> <!--html_end--><br> <img src ="http://www.shnenglu.com/yehao/aggbug/144908.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/yehao/" target="_blank">厚积薄发</a> 2011-04-24 18:47 <a href="http://www.shnenglu.com/yehao/articles/144908.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.uzijav.cn" target="_blank">Ʒ޾þþþþ888</a>| <a href="http://www.bushenba.cn" target="_blank">ھƷþþþӰԺ޹²</a>| <a href="http://www.czzdjsj.cn" target="_blank">һaƬþëƬ</a>| <a href="http://www.etxf.cn" target="_blank">ƷþóӰԺ</a>| <a href="http://www.haole001.cn" target="_blank">99þֻоƷ</a>| <a href="http://www.8xpn.cn" target="_blank">ʵҶ԰׾ʾþ</a>| <a href="http://www.qfzxd.cn" target="_blank">þþþAV</a>| <a href="http://www.eboa.com.cn" target="_blank">þþþþྫƷֱ</a>| <a href="http://www.kq58.cn" target="_blank">þ</a>| <a href="http://www.gz2378.cn" target="_blank">ĻþþƷˮ</a>| <a href="http://www.mztkn.cn" target="_blank">þùƷһ</a>| <a href="http://www.510dpw.cn" target="_blank">˺ݺۺ88ۺϾþ</a>| <a href="http://www.szcybj.cn" target="_blank">Ʒ99þþþþ鶹</a>| <a href="http://www.hao266.cn" target="_blank">þþƷ</a>| <a href="http://www.pic789.cn" target="_blank">޹˾Ʒ91þþ </a>| <a href="http://www.s36mbtm.cn" target="_blank">һɫþۺϺݺƪ</a>| <a href="http://www.kanqiuwang.cn" target="_blank">ݺɫþþۺƵպ</a>| <a href="http://www.lafei02.cn" target="_blank">ҹƷþþþ</a>| <a href="http://www.r3665.cn" target="_blank">ĻhdþþƷ </a>| <a href="http://www.ktze6z.cn" target="_blank">þ޹ƷAVϼ</a>| <a href="http://www.changde8.cn" target="_blank">޾þһح</a>| <a href="http://www.shisanshui.cn" target="_blank">ž99Ʒþþþþ</a>| <a href="http://www.51kuyu.cn" target="_blank">Ů˸߳þþýˮ</a>| <a href="http://www.2mo04k.cn" target="_blank">Ʒþþþþҹҹ </a>| <a href="http://www.hfhyzt.cn" target="_blank">þùһƬѹۿ</a>| <a href="http://www.hgysc.cn" target="_blank">avþþþþòվ</a>| <a href="http://www.hwhack.cn" target="_blank">Ʒþþþþ³</a>| <a href="http://www.ijlm.cn" target="_blank">þþƷƵ</a>| <a href="http://www.hygame8888.cn" target="_blank">þ99Ʒ鶹լլ</a>| <a href="http://www.5ihubei.cn" target="_blank">þ99ۺϾƷŮͬ</a>| <a href="http://www.bhga.cn" target="_blank">þۺϸϾþúݺݺ97ɫ69</a>| <a href="http://www.huanpou.cn" target="_blank">޾ƷþþþþðĦ </a>| <a href="http://www.668j.cn" target="_blank">ƷŮþþ</a>| <a href="http://www.spxian.cn" target="_blank">2021ٸþþþþþþþ</a>| <a href="http://www.by6463.cn" target="_blank">þ66͵Ʒ9</a>| <a href="http://www.pbbu.cn" target="_blank">Ұ¾þþƷ</a>| <a href="http://www.hasgw.cn" target="_blank">þWWW˳һƬ</a>| <a href="http://www.linan521.cn" target="_blank">þþƷ</a>| <a href="http://www.wwohui.cn" target="_blank">þþȹ͵ۺ</a>| <a href="http://www.gsm777.cn" target="_blank">޾Ʒһ߾þ</a>| <a href="http://www.biezhuai.cn" target="_blank">ŷ츾XXXXԾþþ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>