版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Unix多线程学习笔记 Weiming2011-08-05 tydic声明:本文档所有内容均为网上整理所得,不保证所有内容正确性.版权属作者本人所有。不用做任何商业用途,仅供广大网友学习交流之用,如有侵权,请联系本人删除,若发现文档中内容有错误或者有个人见解,欢迎指教与相互讨论.Email:.。第一节 概述多线程程序作为一种多任务、并发的工作方式,有以下的优点1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consu
2、ming)置于一个新的线程,可以避免这种尴尬的情况。2) 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。线程也有很多缺点1) 编写多线程程序需要更全面更升入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性很大。2) 调试一个多线程程序比调试一个单线程函数困难得多。Linux下最常用的是遵循POSIX标准的pthread线程库。pthread的实现是通过系统调用clone()
3、这一Linux特有的系统调用来实现。多线程开发涉及的基本概念主要包括三点:线程,互斥锁,条件.其中线程操作操作又分为线程的创建,退出,等待3种.互斥锁则主要包括4种操作,分别是创建,销毁,加锁和解锁.条件操作有5种操作,创建,销毁,触发,广播和等待.其它的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本操作封装处理.线程基础函数列表线程,互斥锁,条件在 Linux 平台上对应的 API 可以用表 1 归纳。为了方便熟悉 Windows 线程编程的读者熟悉 Linux 多线程开发的 API,我们在表中同时也列出 Windows SDK 库中所对应的 API 名称。表1 线程基础函数列表对象
4、操作Linux Pthread APWindows SDK 库对应 API线程创建pthread_createCreateThread退出pthread_exitThreadExit等待pthread_joinWaitForSingleObject互斥锁创建pthread_mutex_initCreateMutex销毁pthread_mutex_destroyCloseHandle加锁pthread_mutex_lockWaitForSingleObject解锁pthread_mutex_unlockReleaseMutex条件创建pthread_cond_initCreateEvent销毁p
5、thread_cond_destroyCloseHandle触发pthread_cond_signalSetEvent广播pthread_cond_broadcastSetEvent / ResetEvent等待pthread_cond_wait / pthread_cond_timedwaitSingleObjectAndWait第二节 线程2.1 线程的创建与结束2.1 .1) pthread_t线程的标示符类型,pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义typedef unsigned long int pthread_t;2.1.2
6、) pthread_createint pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *);第一个参数为指向线程标识符的指针.第二个参数用了设置线程属性,设置为空指针,这样生成默认属性的线程.第三个参数是线程运行函数的起始地址第四个参数是运行函数的参数.当创建线程成功时,函数返回0,若不为0说明线程创建失败,创建错误返回代码为EAGAIN和EINVAL.前者表示系统限制创建的线程,例如线程数目过多;后者表示第二个参数代表的线程属性值非法,线程创建成功后,新创建的线程运行参数三参数四确定的函
7、数,原来得线程继续运行下一行代码.2.1.3) pthread_join函数pthread_join用来等待一个线程结束.int pthread_join(pthread_t, void *);第一个参数为被等待的线程标识符第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值.这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回.2.1.4) pthread_exit线程除了正常执行结束外,还可通过函数pthread_exit来结束它void pthread_exit(void *);唯一的参数是函数的返回码,只要pthr
8、ead_join中得第二个参数不是NULL,这个值将会传递给pthread_join的第二个参数.要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程返回成功,其余调用pthread_join的线程则返回错误ESRCH.2.1.5) pthread_self该函数获取自身线程的ID.pthread_t pthread_self()2.2 修改线程的属性线程属性结构为pthread_attr_t,在头文件/usr/include/pthread.h中定义.线程属性初始化函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用.线程属性对象包括
9、是否绑定,是否分离,堆栈大小,优先级.默认属性为非绑定,非分离,缺省1M的堆栈,与父进程同样级别的优先级.线程属性函数有:对线程属性初始化/去除初始化int pthread_attr_init(pthread_attr_t *attr);int pthread_attr_destroy(pthread_attr_t *attr);获取/修改线程的分离状态属性int pthread_attr_getdetachstate(const pthread_attr_t * attr,int *detachstate);int pthread_attr_setdetachstate(pthread_at
10、tr_t *attr,int detachstate);2.2.1) 关于线程的绑定线程的绑定,牵扯到另外一个概念,轻进程(LWP:Light Weight Process).轻进程可以理解为内核进程,它位于用户层和系统层之间,系统对线程资源的分配,对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程,启动多少个轻进程,哪些轻进程来控制哪些线程是由系统来控制的,这种状态即称为非绑定的。绑定状况下,则顾名思义,即某个线程固定的绑定到一个轻进程上,被绑定的线程具有较高的响应速度,这是因为cpu时间片的调度是面向轻进程的。绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑
11、定的轻进程的优先级和调度级可以使得绑定的线程满足实时反应之类的要求。设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个时指向属性结构的指针,第二个参数是绑定类型PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)下面的代码即创建一个绑定线程pthread_attr_t attr;pthread_t tid;pthread_attr_init(&attr); /*初始化属性值,均设为默认值*/pthread_attr_setscope(&attr,PTHREAD_SCOPE_SYSTEM);pthread
12、_create(&tid,&attr,(void *)my_function,NULL);2.2.2) 关于线程的状态:分离态/非分离态线程的分离状态决定一个线程以什么样的方式来终止自己,在上面的例子中,我们采用了线程的默认属性,即非分离状态,在这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的资源。而分离线程不是这样的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放资源。应该根据自己的需要,选择合适的分离状态。设置线程分离状态的函数:pthread_attr_setdetachstate(pthr
13、ead_attr_t *attr,int detachstate)第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和PTHREAD_CREAE_JOINABLE(非分离线程)这里要主要的一点,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会,留出
14、足够的时间让函数pthread_create返回。设置一段等待时间,是再多线程编程里常用的方法,都是注意不要使用诸如wait()之类的函数,他们是使整个进程睡眠,并不能解决线程同步的问题.使线程进入分离状态也可用如下函数int pthread_detach(pthread_t tid);2.2.3) 关于线程的优先级线程的优先级,它存放在结构sched_param中,用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行关联,一般来说,我们总是先取出优先级,对取得的值修改后再放回去。线程的优先级取值范围为-2020,值越大,其优
15、先级越低,缺省值为0,这个参数仅当调度策略为实时(SCHED_RR或SCHED_FIFO)时才有效。通过pthread_setschedparam()函数来改变pthread_attr_t attr;pthread_t tid;sched_param param;int newprio = 20;pthtead_attr_init(attr);pthread_attr_getschedparam(&attr,param);param.sched_priority = newprio;pthread_attr_setschedparam(&attr,param);pthrad_create(&t
16、id,&attr,(void *)myfunction,myarg);第三节 互斥锁互斥锁是用来保证一段时间内只有一个线程在执行一段代码pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一个线程释放此互斥锁。A线程先锁定互斥锁1,B线程先锁定互斥锁2,这是就出现了死锁。此时我们可以使用函数pthread_mutex_trylock,它是函数pthread_mutex_lo
17、ck的非阻塞版本,当它发现死锁不可避免时,它会返回相应的信息,程序员可以针对死锁做出相应的处理。另外不同的互斥锁类型对死锁的处理不一样,但最主要的还是要程序员自己在程序设计时注意这一点。pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待互斥锁函数有:初始化一个互斥量int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);给一个互斥量加锁int pthread_mutex_lock(pthread
18、_mutex_t *mutex)加锁,如果失败不阻塞int pthread_mutex_trylock(pthread_mutex_t *mutex) 解锁int pthread_mutex_unlock(pthread_mutex_t *mutex)互斥量用pthread_mutex_t数据类型表示,在使用互斥量之前必须先多它初始化,可以把它置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),方法如下pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER也可以调用pthread_mutex_init函数进行初始化,如
19、果动态的分配互斥量,那么释放内存前需要调用pthread_mutex_destory.销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。互斥锁的属性线程和线程的同步对象(互斥量,读写锁,条件变量)都具有属性。在修改属性前都需要对结构进行初始化,使用完后再把该结构回收。用pthread_mutexattr_init对互斥锁属性初始化,用pthread_mutexattr_destory对互斥锁属性
20、结构回收。int pthread_mutexattr_init(pthread_mutexattr_t *attr);int pthread_mutexattr_destroy( pthread_mutexattr_t *attr );互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择: * PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后
21、按优先级获得锁。这种锁策略保证了资源分配的公平性。 * PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。 * PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。 * PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争pthread_mut
22、exattr_init将属性对象的值初始化为缺省值,并分配属性对象占用的空间。设置互斥锁属性函数:1) int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr, int pshared)用来设置互斥锁变量的作用域,PTHREAD_PROCESS_SHARED: 在多个进程中的线程之间共享互斥锁,可以在共享内存中创建互斥锁.PTHREAD_PROCESS_PRIVATE: 仅有那些由同一个进程创建的线程才能够处理该互斥锁2) int pthread_mutexattr_settype(pthread_mutexattr_t *att
23、r , int type);可用来设置互斥锁的 type 属性, 类型属性的缺省值为 PTHREAD_MUTEX_DEFAULT。PTHREAD_MUTEX_NORMAL此类型的互斥锁不会检测死锁。如果线程在不首先解除互斥锁的情况下尝试重新锁定该互斥锁,则会产生死锁。尝试解除由其他线程锁定的互斥锁会产生不确定的行为。如果尝试解除锁定的互斥锁未锁定,则会产生不确定的行为。PTHREAD_MUTEX_ERRORCHECK此类型的互斥锁可提供错误检查。如果线程在不首先解除锁定互斥锁的情况下尝试重新锁定该互斥锁,则会返回错误。如果线程尝试解除锁定的互斥锁已经由其他线程锁定,则会返回错误。如果线程尝试解
24、除锁定的互斥锁未锁定,则会返回错误。PTHREAD_MUTEX_RECURSIVE如果线程在不首先解除锁定互斥锁的情况下尝试重新锁定该互斥锁,则可成功锁定该互斥锁。 与 PTHREAD_MUTEX_NORMAL 类型的互斥锁不同,对此类型互斥锁进行重新锁定时不会产生死锁情况。多次锁定互斥锁需要进行相同次数的解除锁定才可以释放该锁,然后其他线程才能获取该互斥锁。如果线程尝试解除锁定的互斥锁已经由其他线程锁定,则会返回错误。 如果线程尝试解除锁定的互斥锁未锁定,则会返回错误PTHREAD_MUTEX_DEFAULT如果尝试以递归方式锁定此类型的互斥锁,则会产生不确定的行为。对于不是由调用线程锁定的
25、此类型互斥锁,如果尝试对它解除锁定,则会产生不确定的行为。对于尚未锁定的此类型互斥锁,如果尝试对它解除锁定,也会产生不确定的行为。允许在实现中将该互斥锁映射到其他互斥锁类型之一应用互斥量需要注意的几点1) 互斥量需要时间来进行加锁和解锁。锁住较少互斥量的程序通常运行更快。所以互斥量应尽量少,够用即可,每个互斥量保护的区域应则尽量大。2) 互斥量的本质是串行执行,如果很多线程需要频繁的加锁同一个互斥量,则线程的大部分时间就会在等待,这对性能是有害的,如果互斥量保护的数据(或代码)保护彼此无关的片段,则可以把特大的互斥量分解为几个小互斥量来提高性能,这样任意时刻需要小互斥量的线程就会减少,线程等待
26、时间就会减少,所以互斥量应该足够多,每个互斥量保护的区域应该足够少。第四节 条件变量互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足。它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其他某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般来说,条件变量被用来进行线程间的同步。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待”
27、条件变量的条件成立”而挂起,另一个线程使”条件成立”(给出条件成立信号)。为了防止竞争,防止多个线程同时请求pthread_cond_wait()或者pthread_cond_timedwait()的竞争条件,条件变量的使用总是和一个互斥锁结合在一起。没有条件变量,开发者需要线程不停的轮询以查询条件是否满足,因为线程要不停的忙等,会消耗很多资源。条件变量就是不用轮询而能达到同样目的方法相关的函数如下:intpthread_cond_init(pthread_cond_t*cond,pthread_condattr_t*cond_attr); intpthread_cond_wait(pthre
28、ad_cond_t*cond,pthread_mutex_t*mutex);intpthread_cond_timewait(pthread_cond_t*cond,pthread_mutex*mutex,consttimespec*abstime);intpthread_cond_destroy(pthread_cond_t*cond); intpthread_cond_signal(pthread_cond_t*cond);intpthread_cond_broadcast(pthread_cond_t*cond); /解除所有线程的阻塞条件变量采用的数据类型是pthread_cond_t
29、, 在使用之前必须要进行初始化, 这包括两种方式: 静态: 可以把常量PTHREAD_COND_INITIALIZER给静态分配的条件变量. pthread_cond_t myCond = PTHREAD_COND_INITIALIZER;(用于进程间线程的通信)。动态: pthread_cond_init函数, 在释放动态条件变量的内存空间之前, 要用pthread_cond_destroy对其进行清理.int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);其中cond是一个指向结构pthrea
30、d_cond_t的指针cond_attr是一个指向结构pthread_condattr的指针,如果attr为NULL,那么它将使用缺省的属性来设置所指定的条件变量。结构pthread_condatt_t是条件变量的属性结构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用。默认值是PTHREAD_PROCESS_PRIVATE,即此条件变量被同一进程内的各线程使用。释放一个条件变量函数为int pthread_cond_destroy(pthread_cond_t *cond);函数pthread_cond_wait()使线程阻塞在一个条件变量上。int pthread_con
31、d_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);线程解开mutex指向的锁并被条件变量cond阻塞。线程可以被函数pthread_cond_signal和函数pthread_cond_broadcast唤醒。但是要注意的是,条件变量只是其阻塞和唤醒线程的作用,具体判断条件还需用户给出。线程被唤醒后,它将重新检查条件是否满足,如果不满足,一般来说线程应该仍阻塞在这里,被等待下一次唤醒。这个过程一般用while语句实现另一个用来阻塞线程的函数是int pthread_cond_timedwait(pthread_cond_t *cond,pt
32、hread_mutex_t mytex,const struct timespec *abstime);它比函数pthread_cond_wait()多了一个时间参数,经历abstime段时间后,即使条件变量不满足,阻塞也会解除int pthread_cond_signal(pthread_cond_t *cond);它用来释放被阻塞在条件变量cond上的线程。多个线程阻塞在此条件变量上,哪一个线程被唤醒是由线程的调度策略所决定的。条件的检测是在互斥锁的保护下进行的,如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁,如果另一线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个
33、等待它的线程,重新获得互斥锁,重新评价条件,如果两进程共享可读写的内存,条件变量可以用来实现这两进程间的线程同步。条件变量属性使用条件变量之前要先进行初始化。可以像我们前面那样可静态初始化pthread_cond_t my_condition = PTHREAD_COND_INITIALIZER;也可以利用函数pthread_cond_init动态初始化。条件变量属性类型为pthread_condattr_t,它们由以下函数初始化或摧毁int pthread_condattr_init(pthread_condattr_t *attr);int pthread_condattr_destroy
34、(pthread_condattr_t *attr);一旦一个条件变量属性被初始化了,我们就可以利用下面函数来查看或修改特定属性了。int pthread_condattr_getpshared(const pthread_condattr_t *, int *);int pthread_condattr_setpshared(pthread_condattr_t *, int);值可以是PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED(进程间共享)注意:1) pthread_cond_wait()会阻塞调用的线程,直到特定的条件(condition
35、)满足,当这个线程运行时,mutex会被 加锁,当它阻塞时mutex会自动解锁。当收到信号唤醒线程时,mutex会被线程自动上锁, 当线程完成更新共享数据后,开发者有责任解锁mutex。 2) pthread_cond_signal()用来通知(唤醒)等待在条件变量上的另一线程,在mutex 被加锁后被调用,在完成pthread_cond_wait()运行后必须解锁mutex。 3) 如果多于一个线程处于阻塞状态,应该用pthread_cond_broadcast()代替pthread_cond_signal()。 4) 如果在调用pthread_cond_wait()前先调用pthread_
36、cond_signal()就是逻辑错误。 5) 当使用这些例程时,正确的加锁和解锁一个相关联的mutex是必须的,例如: 在调用pthread_cond_wait()之前没有成功加锁mutex会导致线程不会阻塞在调用 pthread_cond_signal()后没有成功解锁mutex,会导致pthread_cond_wait()一直运行 (保持线程阻塞). 条件变量,互斥锁,信号量的区别1) 互斥锁必须总是由给它上锁的线程解锁,信号量的挂出即不必由执行过它的等待操作的同一进程执行。一个线程可以等待某个给定信号灯,而另一个线程可以挂出该信号灯。2) 互斥锁要么锁住,要么被解开(二值状态,类型二值
37、信号量)。3) 由于信号量有一个与之关联的状态(它的计数值),信号量挂出操作总是被记住。然而当向一个条件变量发送信号时,如果没有线程等待在该条件变量上,那么该信号将丢失。4) 互斥锁是为了上锁而优化的,条件变量是为了等待而优化的,信号灯即可用于上锁,也可用于等待,因而可能导致更多的开销和更高的复杂性个人深入理解:1) 个人对pthread_cond_wait()函数的理解pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t*mutex)函数传入的参数mutex用于保护条件,因为我们在调用pthread_cond_wait时,如果条件不成
38、立我们就进入阻塞,但是进入阻塞这个期间,如果条件变量改变了的话,那我们就漏掉了这个条件。因为这个线程还没有放到等待队列上,所以调用pthread_cond_wait前要先锁互斥量,即调用pthread_mutex_lock(),pthread_cond_wait在把线程放进阻塞队列后,自动对mutex进行解锁,使得其它线程可以获得加锁的权利。这样其它线程才能对临界资源进行访问并在适当的时候唤醒这个阻塞的进程。当pthread_cond_wait返回的时候又自动给mutex加锁。实际上边代码的加解锁过程如下:/*pthread_cond_wait()的使用方法*/pthread_mutex_lo
39、ck(&qlock); /*lock*/pthread_cond_wait(&qready, &qlock); /*block-unlock-wait() return-lock*/pthread_mutex_unlock(&qlock); /*unlock*/*/2) 关于pthread_cond_signal()是否放在pthread_mutex_lock()和phtread_mutex_unlock之间的讨论。有人说可以放在pthread_mutex_lock()和phtread_mutex_unlock之间,也有人说不能放在pthread_mutex_lock()和phtread_mu
40、tex_unlock之间之间,或者都可以。观点1:如果放在pthread_mutex_lock()和pthread_mutex_unlock之间,这个做法有问题,举个例子.简单假设线程1,2,curnum值为1,语句执行顺序如下: T2-;pthread_mutex_lock(&mutex_curnum); T2-;while(curnum) T2-; pthread_cond_wait(&cond_curnum,&mutex_curnum);/*T2解锁,睡眠,等信号*/T1-;pthread_mutex_lock(&mutex_curnum); /*轮T1运行,T1上锁*/ T1-;if
41、(!-curnum) /*条件成立*/T1-; pthread_cond_signal(&cond_curnum); /*T1向线程T2发信号*/T2-;pthread_cond_wait(&cond_curnum,&mutex_curnum); /*T1时间片用完,换T2执行,但发觉不能上锁,因为T1持有锁*/ T1-;pthread_mutex_unlock(&mutex_curnum); /*T1解锁*/问题在于一个条件变量和一个互斥变量,一个线程在持有锁的情况下调用pthread_cond_signal(),则等待条件的线程有可能得不到锁。就上面特定的例子来,配合每条语句的执行顺序,虽
42、然T1调用了pthread_cond_signal(),但是T2 100%不能获得锁。UNIX网络编程第2卷:进程间通信提供了一个改进方法pthread_mutex_lock();判断是否别个线程等待的条件发生,是的话设 发生标志 为 是;pthread_mutex_unlock(); if (发生标志 = 是) pthread_cond_signal(.);观点2:pthread_cond_signal即可以放在pthread_mutex_lock和pthread_mutex_unlock之间,也可以放在pthread_mutex_lock和pthread_mutex_unlock之后,但是
43、各有有缺点。之间:pthread_mutex_lock xxxxxxxpthread_cond_signalpthread_mutex_unlock缺点:在某下线程的实现中,会造成等待线程从内核中唤醒(由于cond_signal)然后又回到内核空间(因为cond_wait返回后会有原子加锁的行为),所以一来一回会有性能的问题。但是在LinuxThreads或者NPTL里面,就不会有这个问题,因为在Linux 线程中,有两个队列,分别是cond_wait队列和mutex_lock队列, cond_signal只是让线程从cond_wait队列移到mutex_lock队列,而不用返回到用户空间,不
44、会有性能的损耗。所以在Linux中推荐使用这种模式。之后:pthread_mutex_lock xxxxxxxpthread_mutex_unlockpthread_cond_signal优点:不会出现之前说的那个潜在的性能损耗,因为在signal之前就已经释放锁了缺点:如果unlock和signal之前,有个低优先级的线程正在mutex上等待的话,那么这个低优先级的线程就会抢占高优先级的线程(cond_wait的线程),而这在上面的放中间的模式下是不会出现的。所以,在Linux下最好pthread_cond_signal放中间,但从编程规则上说,其他两种都可以条件变量示例程序#include
45、 #include pthread_mutex_t mutex;pthread_cond_t cond;void *thread1(void *arg) pthread_cleanup_push(pthread_mutex_unlock, &mutex);/提供函数回调保护while (1)printf(thread1 is runningn);pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex);printf(thread1 applied the conditionn);pthread_mutex_unlock(&mutex
46、);sleep(4);pthread_cleanup_pop(0);void *thread2(void *arg) while (1)printf(thread2 is runningn);pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex);printf(thread2 applied the conditionn);pthread_mutex_unlock(&mutex);sleep(1);int main() pthread_t thid1, thid2;printf(condition variable study!n
47、);pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);pthread_create(&thid1, NULL, (void *) thread1, NULL);pthread_create(&thid2, NULL, (void *) thread2, NULL);do pthread_cond_signal(&cond); while (1);sleep(20);pthread_exit(0);return 0;第五节 线程的私有数据在进程内的所有线程共享相同的地址空间,任何声明为静态或外部的变量,或者在进程堆声明
48、的变量,都可以被进程所有的线程读写,怎么才能使线程拥有自己的私有数据呢?posix提供了一种方法,创建线程键。int pthread_key_create(pthread_key_t *key, void (*destructor)(void*);int pthread_setspecific(pthread_key_t key, const void *value);void * pthread_getspecific(pthread_key_t key);创建一个类型为 pthread_key_t 类型的变量,调用 pthread_key_create() 来创建该变量。该函数有两个参数,
49、第一个参数就是上面声明的 pthread_key_t 变量,第二个参数是一个清理函数,用来在线程释放该线程存储的时候被调用。该函数指针可以设成 NULL ,这样系统将调用默认的清理函数。当线程中需要存储特殊值的时候,可以调用 pthread_setspcific() 。该函数有两个参数,第一个为前面声明的 pthread_key_t 变量,第二个为 void* 变量,这样你可以存储任何类型的值。如果需要取出所存储的值,调用 pthread_getspecific() 。该函数的参数为前面提到的 pthread_key_t 变量,该函数返回 void * 类型的值。示例:#include #in
50、clude #include /* The key used to associate a log file pointer with each thread. */static pthread_key_t thread_log_key;/* Write MESSAGE to the log file for the current thread. */void write_to_thread_log (const char* message) FILE* thread_log = (FILE*) pthread_getspecific (thread_log_key); fprintf (t
51、hread_log, “%sn”, message);/* Close the log file pointer THREAD_LOG. */void close_thread_log (void* thread_log) fclose (FILE*) thread_log);void* thread_function (void* args) char thread_log_filename20; FILE* thread_log; /* Generate the filename for this threads log file. */ sprintf (thread_log_filen
52、ame, “thread%d.log”, (int) pthread_self (); /* Open the log file. */ thread_log = fopen (thread_log_filename, “w”); /* Store the file pointer in thread-specific data under thread_log_key. */ pthread_setspecific (thread_log_key, thread_log); write_to_thread_log (“Thread starting.”); /* Do work here.
53、*/ return NULL;int main () int i; pthread_t threads5; /* Create a key to associate thread log file pointers in thread-specific data. Use close_thread_log to clean up the file pointers. */ pthread_key_create (&thread_log_key, close_thread_log); /* Create threads to do the work. */ for (i = 0; i 5; +i) pthread_create (&(threadsi), NULL, thread_function, NULL); /* Wait for all threads to finish. */ for (i = 0; i 5; +i) pthread_join (threads
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 医院环境清洁消毒策略
- 护理安全中的康复治疗安全管理
- 护理纠纷预防的沟通技巧训练
- 口腔疾病的自我诊断
- 动脉粥样硬化药物治疗优化
- 护理投诉管理中的文化因素分析
- 河北邯郸市2026届高三第一次模拟检测数学试卷(含答案)
- 护理查房、护理会诊和护理病历讨论制度
- 离退休职工思想动态分析与对策
- 道孚县农文旅融合发展综合体验中心项目水土保持方案报告表
- 【MOOC】《大学物理的数学基础》(西南交通大学)章节期末慕课答案
- 抢救落水救人方法
- 国企素质测评考试题及答案
- 矿山车队维修管理办法
- 南京六校联合体2026届高三8月份学情调研考试 地理试卷(含答案)
- 全国2025年10月全国自考中国近代史纲要真题及答案
- 家禽孵化技术详解
- (标准)茶楼股份转让合同协议书
- 医院drg付费培训课件
- 中建土木-基础设施工程安全生产管理标准化图册(试行)
- 消防监控室移交协议书
评论
0/150
提交评论