版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Linux多线程李杰聪世界不是串行的世界不是串行的真实世界中很多事情都是同时发生的,而不是按次真实世界中很多事情都是同时发生的,而不是按次序发生。序发生。随着软硬件的发展计算机也进入了并行时代。随着软硬件的发展计算机也进入了并行时代。程序员也应该做好并行程序设计的准备。程序员也应该做好并行程序设计的准备。处理器发展历史回顾处理器发展历史回顾90年代年代增大指令的并行发射能力,超标量处增大指令的并行发射能力,超标量处理器。理器。90年代末期至年代末期至21世纪初世纪初提高主频提高主频21世纪世纪超线程,多核技术出现,主频不再决超线程,多核技术出现,主频不再决定定CPU性能。性能。处理器种类处理器
2、种类通用处理器:通用处理器:intel, amd生产。生产。网络处理器:摩托罗拉,网络处理器:摩托罗拉, powerpc ,cavium嵌入式处理器:嵌入式处理器:TI(高通),(高通),ARM图像处理器:图像处理器:nvida银行卡银行卡主卡副卡POSPOS账户(¥100)银行卡银行卡POS1POS2if (account=100) account=account-100; return 交易成功 else return 余额不够 刷¥100刷¥100 正确执行(账户归零)正确执行(账户归零)Pos1 Pos1 Pos2 Pos2 Pos1 错误执行(账户透支)错误执行(账户透支)Pos1
3、Pos2 Pos2 Pos2 Pos1 Pos1 时间并发的几个概念并发的几个概念竞争(竞争(race condition):程序执行的结果或者输程序执行的结果或者输出取决于某一段代码的执行次序,次序不同结果不出取决于某一段代码的执行次序,次序不同结果不同。同。竞争是不确定的,偶发性较强。竞争是不确定的,偶发性较强。导致系统异常,数据被破坏。导致系统异常,数据被破坏。难以重现,调试,发现竞争。难以重现,调试,发现竞争。加入调试代码时,竞争会消失?!加入调试代码时,竞争会消失?!银行卡存在数据竞争。银行卡存在数据竞争。并发的几个概念并发的几个概念原子性(原子性(atomic):是系统中最小的一个
4、操作集合。是系统中最小的一个操作集合。银行卡例子中,更新账户余额的操作集合需要时一银行卡例子中,更新账户余额的操作集合需要时一个原子操作。也即个原子操作。也即 是一个原子操作,否则引是一个原子操作,否则引入竞争。导致程序运行结果不确定。入竞争。导致程序运行结果不确定。也可把也可把 作为原子操作,但会降低程序性作为原子操作,但会降低程序性能。能。对竞争的判断同时也是对原子操作范围的界定。对竞争的判断同时也是对原子操作范围的界定。程序设计中,竞争区域判断比想象中要困难很多。程序设计中,竞争区域判断比想象中要困难很多。哲学家问题哲学家问题有有5个哲学家围坐在一张圆桌上。个哲学家围坐在一张圆桌上。每相
5、邻两个哲学家之间有一只筷每相邻两个哲学家之间有一只筷子。子。哲学家除了思考就是吃。哲学家除了思考就是吃。当哲学家饿时,他就先拿起作手当哲学家饿时,他就先拿起作手边的筷子然后再拿右手边的筷子边的筷子然后再拿右手边的筷子 。当有两只筷子时,哲学家开始吃当有两只筷子时,哲学家开始吃饭,否则一直等待另外一只筷子。饭,否则一直等待另外一只筷子。哲学家吃晚饭后,放下筷子,继哲学家吃晚饭后,放下筷子,继续思考。续思考。并发中的问题并发中的问题死锁死锁 (deadlock):两个或者以上的线程互相):两个或者以上的线程互相等待,导致整个系统无法向前推进。等待,导致整个系统无法向前推进。例如:例如:5个哲学家同
6、时拿起自己左手边的筷子,然个哲学家同时拿起自己左手边的筷子,然后等待右手边的筷子。由于所有筷子均被占有,导后等待右手边的筷子。由于所有筷子均被占有,导致没有一个哲学家能进食。致没有一个哲学家能进食。并发中的问题并发中的问题活锁(活锁(livelock):两个或者以上的线程会不断):两个或者以上的线程会不断的执行,不断的改变状态,但整个系统却不朝着最的执行,不断的改变状态,但整个系统却不朝着最终目的前进。终目的前进。活锁一般为了解决死锁引入的。死锁的线程只会等活锁一般为了解决死锁引入的。死锁的线程只会等待,不会改变状态。活锁会改变执行状态,但不会待,不会改变状态。活锁会改变执行状态,但不会朝着最
7、终目标前进。朝着最终目标前进。为了解决哲学家的死锁问题。我们规定:如果哲学为了解决哲学家的死锁问题。我们规定:如果哲学家无法拿到右手边的筷子,那么他必须放下他左手家无法拿到右手边的筷子,那么他必须放下他左手边的筷子。等待一会儿再去尝试拿筷子。边的筷子。等待一会儿再去尝试拿筷子。举例:举例:5个哲学同时拿起左手边筷子,个哲学同时拿起左手边筷子, 发现右手边发现右手边的筷子没有,又同时放下左手上的筷子,如此往复的筷子没有,又同时放下左手上的筷子,如此往复并发中的问题并发中的问题饥饿、饿死(饥饿、饿死(starvation):某些线程永远无法得某些线程永远无法得到执行的机会。到执行的机会。举例:某一
8、哲学家,每次去拿筷子时,左手或者右举例:某一哲学家,每次去拿筷子时,左手或者右手边的筷子被拿走,该哲学家只能一直手边的筷子被拿走,该哲学家只能一直“饿肚子饿肚子”。公平性(公平性(fairness):有些线程得到的执行机会):有些线程得到的执行机会多,多, 有些线程得到的执行机会少。有些线程得到的执行机会少。举例:哲学家在拿筷子时,有些哲学家拿到筷子的举例:哲学家在拿筷子时,有些哲学家拿到筷子的可能性可能比其它哲学家大。比如那些好吃懒做,可能性可能比其它哲学家大。比如那些好吃懒做,整天尝试去拿筷子的哲学家。整天尝试去拿筷子的哲学家。多线程概念多线程概念 进程是资源分配的对象,时间片内存等。进程
9、是资源分配的对象,时间片内存等。 线程是使用资源的实体,一个进程中的多个线程共享内存,全局变量,线程是使用资源的实体,一个进程中的多个线程共享内存,全局变量, 文件文件描述符等。描述符等。 线程有独立的栈和寄存器线程有独立的栈和寄存器线程的优缺点线程的优缺点优点:优点: 上下文切换快上下文切换快 共享数据容易共享数据容易 创建线程速度快创建线程速度快缺点缺点 内存共享会导致互相干扰内存共享会导致互相干扰 一个线程崩溃会导致整个进程崩溃一个线程崩溃会导致整个进程崩溃用户级线程用户级线程线程可以在用户层或者内核层提供线程可以在用户层或者内核层提供在用户层实现线程意味着内核并不知道线程的存在。在用户
10、层实现线程意味着内核并不知道线程的存在。用户层线程库会实现线程的创建、删除、调度。用户层线程库会实现线程的创建、删除、调度。用户层的线程创建比较快用户层的线程创建比较快当某个线程要使用内核时,其余线程都会被挂起。当某个线程要使用内核时,其余线程都会被挂起。内核级线程内核级线程内核直接支持线程内核直接支持线程线程的创建、删除和调度都有内核来做。线程的创建、删除和调度都有内核来做。一个线程等待一个线程等待I/O阻塞时,其余线程可以继续运行。阻塞时,其余线程可以继续运行。创建等操作要进出内核,速度会慢些。创建等操作要进出内核,速度会慢些。目前流行的操作系统都支持内核级线程。目前流行的操作系统都支持内
11、核级线程。多线程模型多线程模型内核线程和用户线程的对应关系内核线程和用户线程的对应关系一个内核线程对应一个内核线程对应n个用户线程(用户级线程)个用户线程(用户级线程)一个内核线程对应一个用户线程(内核级线程)一个内核线程对应一个用户线程(内核级线程)用户级线程用户级线程内核级线程内核级线程多线程中的多线程中的forkfork在在linux系统当一个拥有多线程的进程调用系统当一个拥有多线程的进程调用fork时,时,子进程只会有一个线程!子进程只会有一个线程!fork()多线程中的信号多线程中的信号在在linux系统中信号是用来通知进程特殊事件发生系统中信号是用来通知进程特殊事件发生了了多线程中
12、有哪个线程来处理呢?多线程中有哪个线程来处理呢?信号会被递送给恰当的线程,比如信号会被递送给恰当的线程,比如SIGILL非法指非法指令。令。信号会被递送给进程中任意的线程处理。信号会被递送给进程中任意的线程处理。在多线程环境我们一般指定一个线程专门处理信号,在多线程环境我们一般指定一个线程专门处理信号,其余线程全部屏蔽信号处理。其余线程全部屏蔽信号处理。线程池线程池在程序设计中,并不是来一个任务在程序设计中,并不是来一个任务/请求就创建一请求就创建一个线程,执行完毕线程退出。个线程,执行完毕线程退出。程序初始化时创建一个线程池,线程池中有程序初始化时创建一个线程池,线程池中有n个线个线程。程。
13、当一个请求来时,从线程池中取一个线程,完成请当一个请求来时,从线程池中取一个线程,完成请求后线程再放回线程池。求后线程再放回线程池。节省了频繁创建线程和销毁线程的开销。节省了频繁创建线程和销毁线程的开销。pthreadpthread A POSIX standard (IEEE 1003.1c) API for thread creation and synchronization。 pthread大概有大概有60多个函数,包括线程的创建、终止等。多个函数,包括线程的创建、终止等。 Linux中实现了中实现了pthread线程库线程库 Unix/Widows中也有中也有pthread的实现。的
14、实现。 Linux中使用中使用pthead方法为:方法为: #include gcc main.c lpthread注意:注意:必须加必须加lpthread,不像,不像glibc库,库,gcc不会自动去链不会自动去链接接pthread库。库。线程创建:线程创建:pthread_create()pthread_create()pthread_create用来创建一个线程。用来创建一个线程。成功返回成功返回0,失败返回非,失败返回非0。thread指向内存单元将被设置为新创建的线程指向内存单元将被设置为新创建的线程ID,attr是要创建线程的属性(是要创建线程的属性(NULL为默认属性),为默认属
15、性),start_routine为线程开始执行的函数,为线程开始执行的函数,arg为为start_routine的参数。的参数。线程共享全局变量,在一个线程中改变对另外线程线程共享全局变量,在一个线程中改变对另外线程可见。可见。#include int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void*), void *restrict arg);pthread_create()pthread_create()arg不能是局部变量
16、,否则线程使用该变量时可能不能是局部变量,否则线程使用该变量时可能已经被修改或者不存在了。已经被修改或者不存在了。start_routine的参数和返回值都为的参数和返回值都为void *,如果如果有多个参数,那只能打包成一个结构体了。返回值有多个参数,那只能打包成一个结构体了。返回值不能是局部变量!不能是局部变量!主线程退出,则整个进程结束。主线程退出,则整个进程结束。线程终止线程终止如果进程中的任意线程调用如果进程中的任意线程调用exit、_exit、_Exit,那么整个进程都会被终止。那么整个进程都会被终止。线程从启动函数中返回,返回值是线程的退出码。线程从启动函数中返回,返回值是线程的
17、退出码。线程可以被其他线程取消线程可以被其他线程取消线程调用线程调用pthread_exit退出。退出。pthread_exitpthread_exit调用调用pthread_exit后,线程终止。后,线程终止。参数参数value_ptr会被后来调用会被后来调用pthread_join函函数的线程获得!数的线程获得!pthread_join的第一个参数指定某个线程,第的第一个参数指定某个线程,第二个参数用来接收线程的退出值。二个参数用来接收线程的退出值。 #include void pthread_exit(void *value_ptr); int pthread_join(pthread_
18、t thread, void *value_ptr);线程线程IDIDpthread_self返回调用线程的线程返回调用线程的线程ID。当比较两个线程是否是同一个线程,需要用当比较两个线程是否是同一个线程,需要用pthread_equal.#include pthread_t pthread_self(void);int pthread_equal(pthread_t t1, pthread_t t2);线程属性线程属性 pthread_create的第二个参数就是线程的属性,传的第二个参数就是线程的属性,传入入NULL表示使用默认属性。表示使用默认属性。 可以用过可以用过pthread AP
19、I来修改线程的属性。来修改线程的属性。线程分离属性线程分离属性当一个线程被创建时,系统给它创建一个线程控制当一个线程被创建时,系统给它创建一个线程控制块(块(由由thread id来标识)。如果线程没有设置分来标识)。如果线程没有设置分离属性,那么需要其它线程通过离属性,那么需要其它线程通过pthread_join来回收这个线程控制块。如果设置了分离属性,那来回收这个线程控制块。如果设置了分离属性,那么线程结束时自动释放创建时分配的资源。么线程结束时自动释放创建时分配的资源。pthread_detach用来设置分离属性,但须注意用来设置分离属性,但须注意必须在新创建的线程结束之前!一般来说这个
20、函数必须在新创建的线程结束之前!一般来说这个函数是有新建线程进入启动函数后立马调用的,是有新建线程进入启动函数后立马调用的,pthread_detach(pthread_self()#include int pthread_detach(pthread_t thread);线程属性线程属性pthread_attr_init用默认值初始化线程的属性。用默认值初始化线程的属性。成功返回成功返回0,失败返回非,失败返回非0值。值。pthread_attr_destroy用来销毁用来销毁init分配的内分配的内存。该函数理论上都会执行成功,除非你传入了一存。该函数理论上都会执行成功,除非你传入了一个非
21、法的属性指针。个非法的属性指针。成功返回成功返回0,失败返回非,失败返回非0。#include int pthread_attr_destroy(pthread_attr_t *attr);int pthread_attr_init(pthread_attr_t *attr);线程分离属性线程分离属性 两个函数的第一个参数都是属性。两个函数的第一个参数都是属性。pthread_attr_setdetachstate用来设置分离属性,第二个参数有用来设置分离属性,第二个参数有两个值两个值PTHREAD_CREATE_DETACHED 和和 PTHREAD_CREATE_JOINABLE。pthr
22、ead_attr_getdetachstate用来获取分离状态。用来获取分离状态。 注意:线程一旦设置了分离状态,再调用注意:线程一旦设置了分离状态,再调用pthread_join就会出错。就会出错。#include int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);线程栈属性线程栈属性 进程栈空间需要被不同的线程共享,所以可能会出现栈空进程栈空间需
23、要被不同的线程共享,所以可能会出现栈空间不够的情况。此时可以用间不够的情况。此时可以用malloc从堆中分配内存给线程从堆中分配内存给线程当做栈来使用。当做栈来使用。 pthread_attr_setstack设置堆栈时,第二个参数是内设置堆栈时,第二个参数是内存的起始地址,第三个参数为内存的大小。存的起始地址,第三个参数为内存的大小。 pthread_attr_setstacksize可以在不处理栈分配的情可以在不处理栈分配的情况下,改变线程堆栈的大小。况下,改变线程堆栈的大小。#include int pthread_attr_getstack(const pthread_attr_t *
24、restrict attr, void *restrict stackaddr, size_t *restrict stacksize);int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize); int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);线程栈属性线程栈属性guardsize用来设置栈溢出后,还可以使用的栈用来设置栈溢出后,还可以使用的栈的大小,一般为一个的大小,一般为一个PAGESIZE
25、。也称警戒缓冲。也称警戒缓冲区。区。如果我们对如果我们对stackaddr做了修改做了修改 ,那么系统会假,那么系统会假设我们自己会管理栈,不会提供警戒缓冲区给我们设我们自己会管理栈,不会提供警戒缓冲区给我们使用。相当于设置使用。相当于设置guardsize为为0。 #include int pthread_attr_getguardsize(const pthread_attr_t *restrict attr,size_t *restrict guardsize);int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guar
26、dsize);线程同步线程同步当多个线程共享相同的内存时,就需要确保每个线当多个线程共享相同的内存时,就需要确保每个线程看到一致的数据。程看到一致的数据。如果数据是只读的,那么不存在一致性问题。如果数据是只读的,那么不存在一致性问题。如果线程对数据有读有写,这时候就需要同步机制如果线程对数据有读有写,这时候就需要同步机制来保证数据的一致性。来保证数据的一致性。线程同步线程同步需要一种机制确保需要一种机制确保变量修改时,只有变量修改时,只有一个线程在访问。一个线程在访问。这样就能保证数据这样就能保证数据的一致性。的一致性。互斥量互斥量我们可以使用我们可以使用pthread库提供的互斥量来确保同库
27、提供的互斥量来确保同一时间只有一个线程访问数据。一时间只有一个线程访问数据。互斥量其实就是一种锁,在访问共享数据之前设置互斥量其实就是一种锁,在访问共享数据之前设置这个锁,访问完之后释放这个锁。这个锁,访问完之后释放这个锁。互斥量一旦被加了锁,其他任何线程再也不能在这互斥量一旦被加了锁,其他任何线程再也不能在这个互斥量上加锁,需等到锁被释放。个互斥量上加锁,需等到锁被释放。互斥量互斥量 互斥量的类型为互斥量的类型为pthread_mutex_t,要创建一个互斥量,要创建一个互斥量要么使用静态方式创建要么使用静态方式创建(用用PTHREAD_MUTEX_INITIALIZER 来初始化来初始化)
28、,要么调用函数创建。,要么调用函数创建。 pthread_mutex_init函数的第一个参数接受该函数创函数的第一个参数接受该函数创建的互斥量。第二个参数为互斥量属性,可以为建的互斥量。第二个参数为互斥量属性,可以为NULL。#include int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t * attr);int pthread_mutex_destroy(pthread_mutex_t *mutex);互斥量互斥量pthread_mutex_destroy用来销毁用来销毁由由init函函数创建的
29、互斥量,如果是静态创建的则无需调用数创建的互斥量,如果是静态创建的则无需调用destroy函数。函数。两个函数都是成功返回两个函数都是成功返回0,失败返回非,失败返回非0值。值。互斥量加锁互斥量加锁int pthread_mutex_lock(pthread_mutex_t *mutex); 给互斥量给互斥量mutex加锁。如果此互斥量已经加锁,加锁。如果此互斥量已经加锁,那么调用该函数的线程会被阻塞,如果互斥量没加那么调用该函数的线程会被阻塞,如果互斥量没加锁,调用线程给该互斥量加锁。锁,调用线程给该互斥量加锁。参数参数mutex是是由由init函数创建或者静态初始化。函数创建或者静态初始化
30、。成功返回成功返回0,失败返回非,失败返回非0.互斥量加锁互斥量加锁int pthread_mutex_trylock(pthread_mutex_t *mutex); 只是尝试加锁,看锁是否能加上。只是尝试加锁,看锁是否能加上。如果如果锁锁能加上则返回能加上则返回0,不能加上,不能加上errno设置成设置成EBUSY,错误返回其余值。,错误返回其余值。参数参数mutex跟跟lock函数一样。函数一样。线程可以用线程可以用trylock去检查某个去检查某个mutex是否加锁。是否加锁。如果加锁了,它就先去执行其它任务,以此增加并如果加锁了,它就先去执行其它任务,以此增加并发性!发性!互斥量解锁
31、互斥量解锁int pthread_mutex_unlock(pthread_mutex_t*mutex);释放互斥量释放互斥量成功返回成功返回0,失败返回非,失败返回非0死锁问题死锁问题如果线程已经对互斥量加锁了,再对这个互斥量调如果线程已经对互斥量加锁了,再对这个互斥量调用用pthread_mutex_lock,那么此时该线程被,那么此时该线程被死锁。死锁。假设有假设有A、B两个互斥量,线程两个互斥量,线程A先对先对A互斥量加锁,互斥量加锁,然后线程然后线程B对对B互斥量加锁,然后线程互斥量加锁,然后线程A又去对又去对B互互斥量加锁,而线程斥量加锁,而线程B又去对又去对A互斥量加锁。此时两互
32、斥量加锁。此时两个线程产生死锁。个线程产生死锁。有多个互斥量时,要注意加锁的次序以避免死锁。有多个互斥量时,要注意加锁的次序以避免死锁。读写锁读写锁 读写锁提供了比互斥量更好的并发性。读写锁提供了比互斥量更好的并发性。 互斥量只有加锁和不加锁两个状态,同一时间只允许一个互斥量只有加锁和不加锁两个状态,同一时间只允许一个线程加锁。线程加锁。 读写锁有三个状态:读锁、写锁、不加锁。同一时刻只有读写锁有三个状态:读锁、写锁、不加锁。同一时刻只有一个线程能拥有写锁,但是同一时刻可以有多个线程可以一个线程能拥有写锁,但是同一时刻可以有多个线程可以拥有读锁。拥有读锁。 加了写锁后,任何线程都不能再加锁。加
33、了写锁后,任何线程都不能再加锁。 加了读锁后,任何线程都可以对它加读锁,只有一个能加加了读锁后,任何线程都可以对它加读锁,只有一个能加写锁。写锁。 读写锁适合读比较频繁,写比较少的情况,比如数据库。读写锁适合读比较频繁,写比较少的情况,比如数据库。读写锁读写锁int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); 第一个参数返回读写锁对象,
34、第二个参数使用第一个参数返回读写锁对象,第二个参数使用NULL表示默认属性。表示默认属性。返回返回0表示成功,失败返回非表示成功,失败返回非0。当要释放读写锁时,必须先调用当要释放读写锁时,必须先调用destroy函数!函数!PTHREAD_RWLOCK_INITIALIZER 可以初可以初始化静态分配的读写锁。始化静态分配的读写锁。读写锁读写锁int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_u
35、nlock(pthread_rwlock_t *rwlock); 加读锁时要注意判断返回值,因为加读锁的次数可能有限加读锁时要注意判断返回值,因为加读锁的次数可能有限制!所以加读锁是会失败的。制!所以加读锁是会失败的。 加写锁无须判断返回值,写锁只能加一次。加写锁无须判断返回值,写锁只能加一次。 无论加的是写锁还是读锁,都用无论加的是写锁还是读锁,都用pthread_rwlock_unlock解解锁。锁。 成功返回成功返回0,不成功返回非,不成功返回非0读写锁读写锁int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthre
36、ad_rwlock_trywrlock(pthread_rwlock_t *rwlock); 检查是否能加锁,能加锁返回检查是否能加锁,能加锁返回0,不能加锁返回,不能加锁返回EBUSY,失败返回其余值。,失败返回其余值。tryrdlock和和trywrlock可以提高并发性。可以提高并发性。条件变量条件变量条件变量是另一种同步机制。条件变量是另一种同步机制。多个线程可以等待同一个条件,条件满足时被唤醒。多个线程可以等待同一个条件,条件满足时被唤醒。可以唤醒一个线程,也可以唤醒所有等待的线程。可以唤醒一个线程,也可以唤醒所有等待的线程。条件变量跟互斥量条件变量跟互斥量mutex一起使用。一起使
37、用。条件变量的类型是条件变量的类型是pthread_cond_t条件变量条件变量int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr); int pthread_cond_destroy(pthread_cond_t *cond); 条件变量可以用条件变量可以用PTHREAD_COND_INITIALIZER 静静态初始化。态初始化。 函数函数pthread_cond_init动态的初始化条件变量,第二动态的初始化条件变量,第二个参数为个参数为NULL,使用默认属性。,使用默
38、认属性。 条件变量需要条件变量需要destroy函数释放。函数释放。 成功返回成功返回0,失败返回其余值。,失败返回其余值。条件变量条件变量int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout); pthread_co
39、nd_wait第一个参数为初始化后的条件变量,第一个参数为初始化后的条件变量,第二个参数为初始化后的互斥量。首先会被调用线第二个参数为初始化后的互斥量。首先会被调用线程放入等待条件变量的队列,然后释放程放入等待条件变量的队列,然后释放mutex,直到条件满足时被唤醒,唤醒从函数返回时直到条件满足时被唤醒,唤醒从函数返回时再再对对mutx加锁。加锁。 pthread_cond_timewait比第一个函数多一个参数,这比第一个函数多一个参数,这个参数指定了等待时间。如果时间到了还没个参数指定了等待时间。如果时间到了还没被被唤醒,唤醒,那么返回那么返回ETIMEDOUT条件变量条件变量int pt
40、hread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); 唤醒等待在条件变量上的线程,如果没有线程等待唤醒等待在条件变量上的线程,如果没有线程等待在条件变量上,这个唤醒动作是不会被保留的!在条件变量上,这个唤醒动作是不会被保留的!函数函数pthread_cond_signal只会唤醒其中一个只会唤醒其中一个线程,线程,pthread_cond_broadcast唤醒所有线程。唤醒所有线程。条件变量使用条件变量使用 多个线程等待一个计数器达到一个值时被唤醒。多个线程等待一个
41、计数器达到一个值时被唤醒。 Thread A pthread_mutex_lock(&lock);count +;if (count =MAX_COUNT) pthread_cond_signal( &cond);pthread_mutex_unlock(&lock) Thread Bpthread_mutex_lock(&lock);while (count =MAX_COUNT) pthread_cond_signal( &cond);pthread_mutex_unlock(&lock)Thread Bpthread_mutex_lock(&lock);while (count MAX_
42、COUNT) pthread_mutex_unlock(&lock) pthread_cond_wait(&cond);pthread_mutex_lock(&lock); count-;pthread_mutex_unlock(&lock)条件变量使用条件变量使用 Thread A 当满足条件时候发一个信号。当满足条件时候发一个信号。 ThreadB 先给一个先给一个mutex加锁,以便互斥访问加锁,以便互斥访问count的值。的值。 在一个在一个while循环里等待循环里等待count值达到值达到MAX_COUNT。因为当某个条件满足时,可能会有多个线程被唤醒你,所因为当某个条件满足时,可
43、能会有多个线程被唤醒你,所以需要判断条件是否还满足。以需要判断条件是否还满足。 pthread_cond_wait首先把调用线程放入条件变量的等首先把调用线程放入条件变量的等待队列,然后再释放待队列,然后再释放mutex。当函数返回时,。当函数返回时,mutex又又会被加上锁。会被加上锁。 最后会对最后会对mutex解锁,让其他线程使用解锁,让其他线程使用count变量。变量。互斥量属性互斥量属性 pthread_mutexattr_init用默认的属性互斥量属性初用默认的属性互斥量属性初始化始化attr init函数初始化的互斥量属性必须由函数初始化的互斥量属性必须由pthread_mute
44、xattr_destroy来释放。来释放。 互斥量有互斥量有进程共享进程共享属性和属性和类型类型属性。属性。#include int pthread_mutexattr_init(pthread_mutexattr_t *attr);int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);互斥量属性互斥量属性互斥量进程共享属性互斥量进程共享属性当互斥量只在同一进程的不同线程使用时,当互斥量只在同一进程的不同线程使用时,pshared应该设置为应该设置为PTHREAD_PROCESS_PRIVATE 当互斥量在不同进程间以共享内存方式使用
45、时,当互斥量在不同进程间以共享内存方式使用时,pshared应该设置为应该设置为PTHREAD_PROCESS_SHARED #include int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared);int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,int pshared);互斥量类型属性互斥量类型属性#include int pthread_mutexattr_gettype(const
46、 pthread_mutexattr_t *restrict attr, int *restrict type);int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);读写锁属性读写锁属性读写锁和条件变量只提供了进程共享属性读写锁和条件变量只提供了进程共享属性#include int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);int pt
47、hread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict attr, int *restrict pshared);int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr,int pshared);#include int pthread_condattr_init(pthread_condattr_t *attr);int pthread_condattr_destroy(pthread_condattr_t *attr);int pthread_cond
48、attr_getpshared(const pthread_condattr_t *restrict attr, int *restrict pshared);int pthread_condattr_setpshared(pthread_condattr_t *attr,int pshared);运行一次线程运行一次线程有时候在多线程环境中只想让某个函数执行一次。有时候在多线程环境中只想让某个函数执行一次。pthread_once保证了保证了initfn只会运行一次。只会运行一次。initflag必须是全局或者静态的,不能是局部变量。必须是全局或者静态的,不能是局部变量。而且必须初始化为而且
49、必须初始化为PTHREAD_ONCE_INIT #include pthread_once_t initflag = PTHREAD_ONCE_INIT;int pthread_once(pthread_once_t *initflag, void (*initfn)(void);信号量信号量 #includeint sem_init (sem_t *sem, int pshared, unsigned int value); int sem_destroy(sem_t * sem) sem_init初始化一个无名信初始化一个无名信号号量,量,pshared为为1时,该时,该信号量可以在进程间使用,为信号量可以在进程间使用,为0时只在当前进程中使用。时只在当前进程中使用。value信号灯的初值。信号灯的初值。 sem_destroy释放有释放有sem_init初始化后的信号量。释初始化后的信号量。释放时候必须确保没有任何线程或者进程在使用
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五年度残障人士职业康复服务合同2篇
- 温州职业技术学院《BM概论与实训》2023-2024学年第一学期期末试卷
- 2025年度智能设备租赁服务与技术支持合同2篇
- 二零二五年度金融资产证券化股份质押交易合同3篇
- 2025年度学校窗帘更换及节能环保合同3篇
- 个人财产质押借款协议书(2024年修订)版
- 个人房产抵押贷款协议范本(2024版)版B版
- 渭南师范学院《乐理视唱二》2023-2024学年第一学期期末试卷
- 2024版简易自愿离婚合同书范例一
- 二零二五年度新能源汽车采购合同质量监控与配送管理细则3篇
- DB33T 2570-2023 营商环境无感监测规范 指标体系
- 上海市2024年中考英语试题及答案
- 房屋市政工程生产安全重大事故隐患判定标准(2024版)宣传海报
- 房屋市政工程生产安全重大事故隐患判定标准(2024版)宣传画册
- 垃圾车驾驶员聘用合同
- 2025年道路运输企业客运驾驶员安全教育培训计划
- 南京工业大学浦江学院《线性代数(理工)》2022-2023学年第一学期期末试卷
- 2024版机床维护保养服务合同3篇
- 《论拒不执行判决、裁定罪“执行能力”之认定》
- 工程融资分红合同范例
- 2024年贵州省公务员录用考试《行测》真题及答案解析
评论
0/150
提交评论