信号量与互斥锁的区别,互斥锁,读写锁和条件变量的区别

LOVE C/C++(1)
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 条件变量与互斥锁、信号量的区别
&&&&&& 1.互斥锁必须总是由给它上锁的线程解锁,信号量的挂出即不必由执行过它的等待操作的同一进程执行。一个线程可以等待某个给定信号灯,而另一个线程可以挂出该信号灯。
&&&&&& 2.互斥锁要么锁住,要么被解开(二值状态,类型二值信号量)。
&&&&&& 3.由于信号量有一个与之关联的状态(它的计数值),信号量挂出操作总是被记住。然而当向一个条件变量发送信号时,如果没有线程等待在该条件变量上,那么该信号将丢失。
&&&&&& 4.互斥锁是为了上锁而设计的,条件变量是为了等待而设计的,信号灯即可用于上锁,也可用于等待,因而可能导致更多的开销和更高的复杂性。
ps____呵呵,刚刚从一个博客上看到的,感觉总结 的很好,给大家分享下!
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:9154次
排名:千里之外
原创:16篇
(2)(1)(5)(1)(2)(2)(1)(2)(1)(1)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'上篇文章也蛮好,线程同步之条件变量与互斥锁的结合:
现在有这篇文章:
http://blog.csdn.net/goodluckwhh/article/details/8564319
POSIX定义了一系列同步对象用于同步和互斥。同步对象是内存中的变量属于进程中的资源,可以按照与访问数据完全相同的方式对其进行访问。默认情况下POSIX定义的这些同步对象具有进程可见性,即同步对象只对定义它的进程可见;但是通过修改同步对象的属性可以使得同步对象对不同的进程可见,具体的做法是:
  修改同步对象的属性为PTHREAD_PROCESS_SHARED
  在进程的特殊内存区域--共享内存中创建同步对象
这样创建的同步对象将对共享该共享内存的所有进程可见,这些进程可以使用该同步对象进行同步互斥。其中设置共享对象的属性为PTHREAD_PROCESS_SHARED是为了告诉系统该共享对象是跨越进程的,不仅仅对创建它的进程可见;但是仅有这一个条件显然无法满足不同进程使用该同步对象的需求,因为每个进程的地址空间是独立的,位于一个进程的普通内存区域中的对象是无法被其它进程所访问的,能满足这一要求的内存区域是共享内存,因而同步对象要在进程的共享内存区域内创建。同步对象还可以放在文件中。同步对象可以比创建它的进程具有更长的生命周期。
POSIX定义的同步对象包括:
对于这些同步对象,有一些共同点:
每种类型的同步对象都有一个init的API,它完成该对象的初始化,在初始化过程中会分配该同步对象所需要的资源(注意是为支持这种锁而需要的资源,不包括表示同步对象的变量本身所需要的内存)
每种类型的同步对象都一个destory的API,它完成与init相反的工作
对于使用动态分配内存的同步对象,在使用它之前必须先调用init
在释放使用动态分配内存的同步对象所使用的内存时,必须先调用destory释放系统为其申请的资源
每种同步对象的默认作用范围都是进程内部的线程,但是可以通过修改其属性为PTHREAD_PROCESS_SHARED并在进程共享内存中创建它的方式使其作用范围跨越进程范围
无论是作用于进程内的线程,还是作用于不同进程间的线程,真正参与竞争的都是线程(对于不存在多个线程的进程来说就是其主线程),因而讨论都基于线程来
这些同步对象都是协作性质的,相当于一种君子协定,需要相关线程主动去使用,无法强制一个线程必须使用某个同步对象
总体上来说,可以将它们分为两类:&
第一类是互斥锁、读写锁、自旋锁,它们主要是用来保护临界区的,也就是主要用于解决互斥问题的,当尝试上锁时大体上有两种情况下会返回:上锁成功或出错,它们不会因为出现信号而返回。另外解锁只能由锁的拥有者进行。
第二类是条件变量和信号量,它们提供了异步通知的能力,因而可以用于同步和互斥。但是二者又有区别:
信号量可以由发起P操作的线程发起V操作,也可以由其它线程发起V操作;但是条件变量一般要由其它线程发起signal(即唤醒)操作
由于条件变量并没有包含任何需要检测的条件的信息,因而对这个条件需要用其它方式来保护,所以条件变量需要和互斥锁一起使用,而信号量本身就包含了相关的条件信息(一般是资源可用量),因而不需要和其它方式一起来使用
类似于三种锁,信号量的P操作要么成功返回,要么失败返回,不会因为出现信号而返回;但是条件变量可能因为出现信号而返回,这也是因为它没包含相关的条件信息而导致的。(注意:无名信号量也会被信号中断,见:)
一、互斥锁
如果有多个线程在等待一个互斥锁,则在持有互斥锁的线程释放锁后锁将被等待锁的线程中具有最高优先级的那个获得,如果最高优先级线程有多个,则这些线程中谁将获得锁是不确定的。
1)初始化互斥锁
如果互斥锁变量是静态的则可以直接用PTHREAD_MUTEX_INITIALIZER来初始化它,比如:static pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER如果互斥锁变量是动态分配的,则必须在使用它之前用pthread_mutex_init来初始化它.
int&pthread_mutex_init(pthread_mutex_t&*mp,&const&pthread_mutexattr_t&*mattr);&成功返回0,其它返回值表示出错 &
pthread_mutex_init用于初始化互斥锁,如果mattr为NULL则用缺省值初始化由mp所指向的互斥锁,否则使用指定的mattr初始化互斥锁。使用PTHREAD_MUTEX_INITIALIZER与动态分配具有null 属性的 pthread_mutex_init等效,不同之处在于PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查。如果使用pthread_mutex_init初始化互斥锁,并且指定的mattr具有PTHREAD_MUTEX_ROBUST_NP属性,则互斥锁所使用的内存必须在调用pthread_mutex_init之前被清0.在有线程正在使用互斥锁时,不能重新初始化互斥锁或销毁它。
1)使互斥保持一致
如果一个互斥锁的持有者没有释放该锁退出了,则在默认情况下当其它线程再去获取这个锁的时候,就会阻塞从而造成死锁。可以更改互斥锁的属性来改变这种默认的方式:  pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);  pthread_mutexattr_setrobust_np(&mattr,PTHREAD_MUTEX_ROBUST_NP);通过设置锁的上面两个属性,互斥锁就不再具有默认的行为,当一个锁的owner死掉后,其它线程再去获取这个锁的时候,不会被阻塞,而是会获得这个错,但是同时会得到一个EOWNERDEAD的错误。然后获得锁的线程可以尝试处理这个错误:
首先调用pthread_mutex_consistent_np函数来恢复该锁的一致性,
然后调用pthread_mutex_unlock来解锁
接下来在调用加锁
这样该锁的行为就恢复正常了。如果pthread_mutex_consistent_np在恢复锁的一致性时候没有成功,步骤c就不能再执行了,锁也不能被使用了,而且接下来的线程在获取锁都无法获得该锁,而是只能得到返回值ENOTRECOVERABLE。如果获取某个锁的时候得到了ENOTRECOVERABLE的错误,就意味这这个锁不能被使用了,此时只能调用pthread_mutex_destroy销毁互斥锁然后再调用pthread_mutex_int重新初始化该互斥锁,之后才能再使用该互斥锁。
3)锁定(获取)互斥锁
pthread_mutex_lock可以锁定指定的互斥锁。当它返回时,该互斥锁已被锁定,调用它的线程就获得了这个互斥锁。如果该互斥锁已被另一个线程锁定和拥有,则调用线程将阻塞,直到该互斥锁变为可用为止。
4)解除互斥锁锁定(释放互斥锁)
pthread_mutex_unlock可以解除指定互斥锁的锁定即释放互斥锁。
5)尝试锁定(获取)互斥锁
pthread_mutex_trylock可以尝试锁定指定的互斥锁。pthread_mutex_trylock是 pthread_mutex_lock的非阻塞版本。如果 mutex 所引用的互斥对象当前被任何线程锁定,则将立即返回该调用。否则,该互斥锁将被锁定,调用线程成为其持有者。
6)销毁互斥锁
pthread_mutex_destroy可以销毁与指定的互斥锁相关联的任何状态。
7)初始化互斥锁属性对象
互斥锁具有一些属性,通过修改这些属性可以控制锁的一些行为。缺省的互斥锁属性及其值如下:
pshared: & & & & &PTHREAD_PROCESS_PRIVATE
type: & & & & & & & & &PTHREAD_MUTEX_DEFAULT
protocol: & & & & & PTHREAD_PRIO_NONE
prioceiling: & & & –&
robustness: & &PTHREAD_MUTEX_STALLED_NP
可以用pthread_mutexattr_init将与互斥锁对象相关联的属性初始化为其缺省值。pthread_mutexattr_init的参数类型实际上是opaque的,其中包含一个由系统分配的属性对象。该函数执行过程中会为属性对象分配所需的内存,因而如果未通过pthread_mutexattr_destroy销毁互斥锁属性对象时就会导致内存泄漏。对于互斥锁属性对象,必须首先通过调用pthread_mutexattr_destroy将其销毁,才能重新初始化该对象。
9)设置/获取互斥锁的作用域属性
函数pthread_mutexattr_setpshared用来设置互斥锁的作用域。互斥锁变量可以是进程专用的变量,也可以是跨越进程边界的变量。范围属性的取值及其含义:
PTHREAD_PROCESS_SHARED:具有该属性的互斥锁可以在多个进程中的线程之间共享。
PTHREAD_PROCESS_PRIVATE:只有创建本互斥锁的线程所在的进程内的线程才能够使用该互斥锁变量。该值是缺省值。
函数pthread_mutexattr_getpshared可用来返回由 pthread_mutexattr_setpshared设置的互斥锁变量的范围。
10)设置/获取互斥锁的类型属性
pthread_mutexattr_settype用来设置指定互斥锁的类型属性。类型属性的缺省值为 PTHREAD_MUTEX_DEFAULT。互斥锁的类型及其行为:
PTHREAD_MUTEX_NORMAL:不提供死锁检测。尝试重新锁定互斥锁会导致死锁。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或未锁定,则将产生不确定的行为。
PTHREAD_MUTEX_ERRORCHECK:提供错误检查。如果某个线程尝试重新锁定的互斥锁已经由该线程锁定,则将返回错误。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或者未锁定,则将返回错误。
PTHREAD_MUTEX_RECURSIVE:该互斥锁会保留锁定计数这一概念。线程首次成功获取互斥锁时,锁定计数会设置为 1。线程每重新锁定该互斥锁一次,锁定计数就增加 1。线程每解除锁定该互斥锁一次,锁定计数就减小 1。 锁定计数达到 0 时,该互斥锁即可供其他线程获取。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或者未锁定,则将返回错误。
PTHREAD_MUTEX_DEFAULT:尝试以递归方式锁定该互斥锁将产生不确定的行为。对于不是由调用线程锁定的互斥锁,如果尝试解除对它的锁定,则会产生不确定的行为。如果尝试解除锁定尚未锁定的互斥锁,则会产生不确定的行为。
对比下信号,信号可以做到通知其它线程某件事发生了,接收信号的线程只需要注册一个信号处理函数,然后信号发生后该处理函数就会被系统调用,一旦该函数被调用了就意味着注册时关联的信号所代表的事情发生了。但要注意:
POSXI要求多线程应用中信号处理程序必须在应用的多个线程之间共享(即在一个进程的多个线程之间共享),因而对于同一个进程中的多个线程来说它们必须共享信号处理程序,信号处理程序无法确定信号是被发给谁的
使用信号时只需要注册信号处理程序即可,不需要创建某种同步对象,而使用条件变量需要创建同步对象,如果要在进程间进行同步和互斥还对条件变量的作用域和属性有要求
有权限的任何用户的任何程序都可以发送信号给一个线程,而使用条件变量时,相关的线程必须可以访问同步对象
4)在指定的时间之前阻塞
pthread_cond_timedwait的用法与 pthread_cond_wait的用法基本相同,区别在于在由abstime指定的时间之后不再被阻塞。pthread_cond_reltimedwait_np与pthread_cond_timedwait基本相同,它们唯一的区别在于pthread_cond_reltimedwait_np使用相对时间间隔而不是将来的绝对时间作为其最后一个参数的值。类似于pthread_cond_wait,pthread_cond_reltimedwait_np和pthread_cond_timedwait也是取消点。
1.基本概念
自旋锁是SMP架构中的一种low-level的同步机制。当线程A想要获取一把自选锁而该锁又被其它线程锁持有时,线程A会在一个循环中自选以检测锁是不是已经可用了。对于自选锁需要注意:
由于自旋时不释放CPU,因而持有自旋锁的线程应该尽快释放自旋锁,否则等待该自旋锁的线程会一直在那里自旋,这就会浪费CPU时间。
持有自旋锁的线程在sleep之前应该释放自旋锁以便其它线程可以获得自旋锁。(在内核编程中,如果持有自旋锁的代码sleep了就可能导致整个系统挂起,最近刚解决了一个内核中的问题就是由于持有自旋锁时sleep了,然后导致所有的核全部挂起(是一个8核的CPU))
使用任何锁需要消耗系统资源(内存资源和CPU时间),这种资源消耗可以分为两类:
建立锁所需要的资源
当线程被阻塞时锁所需要的资源
对于自旋锁来说,它只需要消耗很少的资源来建立锁;随后当线程被阻塞时,它就会一直重复检查看锁是否可用了,也就是说当自旋锁处于等待状态时它会一直消耗CPU时间。
对于自旋锁来说,它只需要消耗很少的资源来建立锁;随后当线程被阻塞时,它就会一直重复检查看锁是否可用了,也就是说当自旋锁处于等待状态时它会一直消耗CPU时间。对于互斥锁来说,与自旋锁相比它需要消耗大量的系统资源来建立锁;随后当线程被阻塞时,线程的调度状态被修改,并且线程被加入等待线程队列;最后当锁可用时,在获取锁之前,线程会被从等待队列取出并更改其调度状态;但是在线程被阻塞期间,它不消耗CPU资源。
因此自旋锁和互斥锁适用于不同的场景。自旋锁适用于那些仅需要阻塞很短时间的场景,而互斥锁适用于那些可能会阻塞很长时间的场景。
POSIX定义的自旋锁的数据类型是: pthread_spinlock_t相关API&
#include&&pthread.h&&&
int&pthread_spin_init(pthread_spinlock_t&*lock,&int&pshared);成功返回0,其它返回值表示出错&&
int&pthread_spin_lock(pthread_spinlock_t&*lock);成功返回0,其它返回值表示出错&&
int&pthread_spin_trylock(pthread_spinlock_t&*lock);成功返回0,其它返回值表示出错&&
int&pthread_spin_unlock(pthread_spinlock_t&*lock);成功返回0,其它返回值表示出错&&
int&pthread_spin_destroy(pthread_spinlock_t&*lock);成功返回0,其它返回值表示出错&&
1)初始化自旋锁
pthread_spin_init用来申请使用自旋锁所需要的资源并且将它初始化为非锁定状态。pshared的取值及其含义:
PTHREAD_PROCESS_SHARED:该自旋锁可以在多个进程中的线程之间共享。
PTHREAD_PROCESS_PRIVATE:仅初始化本自旋锁的线程所在的进程内的线程才能够使用该自旋锁。
2)获得一个自旋锁
pthread_spin_lock用来获取(锁定)指定的自旋锁. 如果该自旋锁当前没有被其它线程所持有,则调用该函数的线程获得该自旋锁. 否则该函数在获得自旋锁之前不会返回。如果调用该函数的线程在调用该函数时已经持有了该自旋锁,则结果是不确定的。
3)尝试获取一个自旋锁
pthread_spin_trylock会尝试获取指定的自旋锁,如果无法获取则理解返回失败
4)释放(解锁)一个自旋锁
pthread_spin_unlock用于释放指定的自旋锁
5)销毁一个自旋锁
pthread_spin_destroy用来销毁指定的自旋锁并释放所有相关联的资源(所谓的所有指的是由pthread_spin_init自动申请的资源)在调用该函数之后如果没有调用pthread_spin_init重新初始化自旋锁,则任何尝试使用该锁的调用的结果都是未定义的。如果调用该函数时自旋锁正在被使用或者自旋锁未被初始化则结果是未定义的。
另外,网上找了蛮久,也没有找到合适的利用共享内存的mutex跨进程共享应用,只找到父子进程间的mutex跨进程应用。
可能的确跨进程而言,mutex不如信号量、共享内存等来得更方便吧。
#include &pthread.h&
#include &stdio.h&
#include &stdlib.h&
#include &sys/mman.h&
#include &unistd.h&
#include &sys/types.h&
#include &sys/wait.h&
typedef struct _FOO
}FOO,*PFOO;
int main(int argc,char *argv[])
pthread_mutexattr_
pthread_mutex_
pthread_mutexattr_init(&mutexattr);
pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_SHARED);
//设置为进程共享
pthread_mutex_init(&mutex,&mutexattr);
ptr = (PFOO)mmap(NULL,sizeof(FOO),PROT_READ | PROT_WRITE,MAP_SHARED|MAP_ANON,-<span style="color: #,<span style="color: #);
//匿名内存映射,让父子进程都操作ptr指向的内存区,如果不使用共享内存,则父子进程的ptr指向的是各自的内存空间
ptr-&nCount = <span style="color: #;
ptr-&nData = <span style="color: #;
printf("%d,%d\n",ptr-&nCount,ptr-&nData);
if( (pid = fork()) & <span style="color: #)
printf("fork error\n");
return -<span style="color: #;
else if( <span style="color: # == pid)
for(int i = <span style="color: #;i&<span style="color: #;i++)
pthread_mutex_lock(&mutex);
ptr-&nCount++;
printf("child ++ === %d\n",ptr-&nCount);
pthread_mutex_unlock(&mutex);
usleep(<span style="color: #00);
for(int i = <span style="color: #;i&<span style="color: #;i++)
pthread_mutex_lock(&mutex);
ptr-&nCount += <span style="color: #;
printf("parent +2 === %d\n",ptr-&nCount);
pthread_mutex_unlock(&mutex);
usleep(<span style="color: #00);
waitpid(pid,NULL,<span style="color: #);
munmap(NULL,sizeof(FOO));
return <span style="color: #;
阅读(...) 评论()操作系统(11)
共享数据(全局变量或堆变量)的自增(&#43;&#43;)操作在多线程环境下会出现错误是因为这个操作(一条c语句)被编译为汇编代码后不止一条指令,因此在执行的时候可能执行了一半就被调度系统打断,去执行别的代码。
我们把单指令的操作称为原子的(Atomic),因为无论如何,单条指令的执行是不会被打断的。为了避免出错,很多体系结构都提供了一些常用操作的原子指令,例如i386就有一条inc指令可以直接增加一个内存单元&#20540;。
在Windows里,有一套API专门进行一些原子操作(见下表),这些API称为InterlockedAPI。
Windows API & & & & & & & & & & & 作用
InterlockedExchange & & 原子地交换两个&#20540;
InterlockedDecrement & &原子地减少一个&#20540;
InterlockedIncrement & &原子地增加一个&#20540;
InterlockedXor & & & & 原子地进行异或操作
使用这些函数时,Windows将保证是原子操作的,因此可以不用担心出现问题。遗憾的是,尽管原子操作指令非常方便,但是它们仅仅适用于比较简单的特定场合。在复杂的场合下,比如我们要保证一个复杂的数据结构更改的原子性,原子操作就力不从心了。这里我们就需要更加通用的手段:锁。
为了避免多个线程同时读写同一个数据(全局变量或堆变量)而产生不可预料的后果,我们需要将各个线程对同一个数据的访问同步(Synchronization)。所谓同步,即是指在一个线程访问数据未结束的时候,其他线程不得对同一个数据进行访问。如此,对数据的访问被原子化了。
同步的最常见方法是使用锁(Lock)。即每个线程在访问数据或资源之前首先试图获取(Acquire)锁,并在访问结束后释放(Release)锁。在锁已经被占用的时候试图获取锁时,线程会等待,直到锁重新可用。
二元信号量(Binary Semaphore)是最简单的一种锁,它只用两种状态:占用与非占用。它适合只能被唯一一个线程访问的资源。当二元信号量处于非占用状态时,第一个试图获取该二元信号量的线程会获得该锁,并将二元信号量置为占用状态,此后其他的所有试图获取该二元信号量的线程将会等待,知道该锁被释放。
对于允许多个线程并发访问的资源,多元信号量简称为信号量(Semaphore),它是一个很好的选择。一个初始&#20540;为N的信号量允许N个线程并发访问。线程访问资源的时候首先获取信号量,进行如下操作:
■将信号量的&#20540;减1
■如果信号量的&#20540;小于0,则进入等待状态,否则继续执行。
访问资源之后,线程释放信号量,进行如下操作:
■将信号量的&#20540;加1.
■如果信号量的&#20540;小于1,唤醒一个等待中的线程。
互斥量(Mutex)和二元信号量很类&#20284;,即资源仅同时允许一个线程访问,但和信号量不同的是,信号量在整个系统可以被任意线程获取并释放,也就是说,同一个信号量可以被系统中的一个线程获取之后由另一个线程释放。而互斥量则要求哪个线程获取了互斥量,哪个线程就要负责释放这个锁,其他线程越俎代庖去释放互斥量是无效的。
临界区(Critical Section)是比互斥量更加严&#26684;的同步手段。在术语中,把临界区的锁的获取称为进入临界区,而把锁的释放称为离开临界区。临界区和互斥量与信号量的区别在于,互斥量和信号量在系统中任何进程里都是可见的,也就是说,一个进程创建了一个互斥量或信号量,另一个进程试图去获取该锁时合法的。然而,临界区的作用范围仅限于本进程中,其他的进程无法获取该锁(类&#20284;于静态全局变量对全局变量)。除此之外,临界区具有和互斥量相同的性质。
读写锁(Read-Write Lock)致力于一种更加特定的场合的同步。对于一段数据,多个线程同时读取总是没问题的,但假设操作都不是原子型,只要有任何一个线程试图对这个数据进行修改,就必须使用同步手段来避免出错。如果我们使用上述信号量、互斥量或临界区中的任何一种来进行同步,尽管可以保证程序争取,但对于读取频繁,而仅仅偶尔写入的情况,会显得非常低效。读写锁可以避免这个问题。对于同一个锁,读写锁由两种获取方式,共享的(Shared)或独占的(Exclusive)。当锁处于自由的状态时,试图以任何一种方式获取锁都能成功,并将锁置于对应的状态。如果锁处于共享状态,其他线程以共享的方式获取锁仍然会成功,此时这个锁分配给了多个线程。然而,如果其他线程试图以独占的方式获取已经处于共享状态的锁,那么它必须等待锁被所有的线程释放。相应地,处于独占状态的锁将阻止任何其他线程获取该锁,不论它们试图以哪种方式获取。读写锁的行为可以总结为如下表:
读写锁状态 & & & & &&以共享方式获取 & & & & & & & &以独占方式获取
自由 & & & & & & & & & & &成功 & & & & & & & & & & & & & & & & &成功
共享 & & & & & & & & & & &成功 & & & & & & & & & & & & & & & & &等待
独占 & & & & & & & & & & &等待 & & & & & & & & & & & & & & & & &等待
条件变量(Condition Variable)作为一种同步手段,作用类&#20284;于一个栅栏。对于条件变量,线程可以有两种操作,首先线程可以等待条件变量,一个条件变量可以被多个线程等待。其次,线程可以唤醒条件变量,此时某个或所有等待此条件变量的线程都会被唤醒并继续支持。也就是说,使用条件变量可以让许多线程一起等待某个事件的发生,当事件发生时(条件变量被唤醒),所有的线程可以一起恢复执行。
转自&/qinfengxiaoyue/item/75c0ddfd13b08f1bfe3582b4
与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。
条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待&条件变量的条件成立&而挂起;另一个线程使&条件成立&(给出条件成立信号)。
条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。
使用条件变量之前要先进行初始化。可以在单个语句中生成和初始化一个条件变量如:pthread_cond_t my_condition=PTHREAD_COND_INITIALIZER;(用于进程间线程的通信)。可以利用函数pthread_cond_init动态初始化。
条件变量分为两部分: 条件和变量. 条件本身是由互斥量保护的. 线程在改变条件状态前先要锁住互斥量. 它利用线程间共享的全局变量进行同步的一种机制。
相关的函数如下:
1intpthread_cond_init(pthread_cond_t*cond,pthread_condattr_t*cond_attr);&&&2
intpthread_cond_wait(pthread_cond_t*cond,pthread_mutex_t*mutex);3
intpthread_cond_timewait(pthread_cond_t*cond,pthread_mutex*mutex,consttimespec*abstime);4
intpthread_cond_destroy(pthread_cond_t*cond);5
intpthread_cond_signal(pthread_cond_t*cond);6
intpthread_cond_broadcast(pthread_cond_t*cond);//解除所有线程的阻塞
简要说明:&&&&&
&&&&& (1)初始化.init()或者pthread_cond_t cond=PTHREAD_COND_INITIALIER;属性置为NULL
&&&&& (2)等待条件成立.pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真
&&&&& timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
&&&&& (3)激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
&&&&& (4)清除条件变量:无线程等待,否则返回EBUSY
1. 初始化:
&&& 条件变量采用的数据类型是pthread_cond_t, 在使用之前必须要进行初始化, 这包括两种方式:
静态: 可以把常量PTHREAD_COND_INITIALIZER给静态分配的条件变量.动态: pthread_cond_init函数, 是释放动态条件变量的内存空间之前, 要用pthread_cond_destroy对其进行清理.
#include &pthread.h&int
pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr);int pthread_cond_destroy(pthread_cond_t *cond);成功则返回0,
出错则返回错误编号.
&&& 当pthread_cond_init的attr参数为NULL时, 会创建一个默认属性的条件变量; 非默认情况以后讨论.
2. 等待条件:
#include &pthread.h&int
pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restric mutex);int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t
*restrict mutex, const struct timespec *restrict timeout);成功则返回0,
出错则返回错误编号.
&&& 这两个函数分别是阻塞等待和超时等待.
&&& 等待条件函数等待条件变为真, 传递给pthread_cond_wait的互斥量对条件进行保护, 调用者把锁住的互斥量传递给函数. 函数把调用线程放到等待条件的线程列表上, 然后对互斥量解锁, 这两个操作是原子的. 这样便关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道, 这样线程就不会错过条件的任何变化.
&&& 当pthread_cond_wait返回时, 互斥量再次被锁住.
3. 通知条件:
#include &pthread.h&int
pthread_cond_signal(pthread_cond_t *cond);int pthread_cond_broadcast(pthread_cond_t *cond);成功则返回0,
出错则返回错误编号.
&&& 这两个函数用于通知线程条件已经满足. 调用这两个函数, 也称向线程或条件发送信号. 必须注意, 一定要在改变条件状态以后再给线程发送信号.
#include &stdio.h&
#include &pthread.h&
pthread_mutex_
pthread_cond_
void *thread1(void *arg)
pthread_cleanup_push(pthread_mutex_unlock, &mutex);
//提供函数回调保护
while (1) {
printf(&thread1 is running\n&);
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
printf(&thread1 applied the condition\n&);
pthread_mutex_unlock(&mutex);
pthread_cleanup_pop(0);
void *thread2(void *arg)
while (1) {
printf(&thread2 is running\n&);
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
printf(&thread2 applied the condition\n&);
pthread_mutex_unlock(&mutex);
int main()
pthread_t thid1, thid2;
printf(&condition variable study!\n&);
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
pthread_create(&thid1, NULL, (void *) thread1, NULL);
pthread_create(&thid2, NULL, (void *) thread2, NULL);
pthread_cond_signal(&cond);
} while (1);
sleep(20);
pthread_exit(0);
条件变量与互斥锁、信号量的区别
&&&&&& 1.互斥锁必须总是由给它上锁的线程解锁,信号量的挂出即不必由执行过它的等待操作的同一进程执行。一个线程可以等待某个给定信号灯,而另一个线程可以挂出该信号灯。
&&&&&& 2.互斥锁要么锁住,要么被解开(二&#20540;状态,类型二&#20540;信号量)。
&&&&&& 3.由于信号量有一个与之关联的状态(它的计数&#20540;),信号量挂出操作总是被记住。然而当向一个条件变量发送信号时,如果没有线程等待在该条件变量上,那么该信号将丢失。
&&&&&& 4.互斥锁是为了上锁而设计的,条件变量是为了等待而设计的,信号灯即可用于上锁,也可用于等待,因而可能导致更多的开销和更高的复杂性。
转自/newlist/archive//2346284.html
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:128736次
积分:3056
积分:3056
排名:第11933名
原创:173篇
转载:63篇
(47)(39)(25)(2)(7)(27)(37)(49)(3)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'}

我要回帖

更多关于 条件变量和互斥锁 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信