条件变量、信号量、互斥锁

转载 http://blog.csdn.net/yusiguyuan/article/details/14161225

线程间的同步技术,主要以互斥锁和条件变量为主,条件变量和互斥所的配合使用可以很好的处理对于条件等待的线程间的同步问题。举个例子:当有两个变量x,y需要在多线程间同步并且学要根据他们之间的大小比较来启动不同的线程执行顺序,这便用到了条件变量这一技术。看代码

 1 #include <iostream>
 2 #include <pthread.h>
 3 using namespace std;
 4
 5 pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
 6 pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
 7 pthread_t tid1,tid2,tid3;
 8
 9 int x = 10;
10 int y = 20;
11
12
13 void *thrd_1(void *arg)
14 {
15     pthread_mutex_lock(&qlock);
16     while(x<y)
17     {
18         pthread_cond_wait(&qready,&qlock);
19     }
20     pthread_mutex_unlock(&qlock);
21     cout<<"1"<<endl;
22     sleep(5);
23 }
24
25 void *thrd_2(void *arg)
26 {
27     pthread_mutex_lock(&qlock);
28     x = 20;
29     y = 10;
30     cout<<"has change x and y"<<endl;
31
32     pthread_mutex_unlock(&qlock);
33     if(x > y)
34     {
35         pthread_cond_signal(&qready);
36     }
37     cout<<"2"<<endl;
38 }
39
40 void *thrd_3(void *arg)
41 {
42     pthread_join(tid1,NULL);
43     cout<<"3"<<endl;
44 }
45
46 int main(int argc,char **argv)
47 {
48     int err;
49     err = pthread_create(&tid1,NULL,thrd_1,NULL);
50     if(err != 0)
51     {
52         cout<<"pthread 1 create error"<<endl;
53     }
54     err = pthread_create(&tid2,NULL,thrd_2,NULL);
55     if(err != 0)
56     {
57         cout<<"pthread 2 create error"<<endl;
58     }
59     err = pthread_create(&tid3,NULL,thrd_3,NULL);
60     if(err != 0)
61     {
62         cout<<"pthread 3 create error"<<endl;
63     }
64     while(1)
65     {
66         sleep(1);
67     }
68     return 0;
69
70 }

可以看到,创建了3个线程后,执行顺序2,1,3,即打印出的数字是213。为什么是这个顺序呢?我们接下去看,当创建tid1线程的时候,进入线程函数,并且加上了锁,然后进入pthread_cond_wait函数,这个函数的功能是等待qready这个条件变量成功,这个条件是什么呢?我们稍后在看,现在我们只要知道,这个函数在qready条件没满足的时候会卡在这里,并且,会把传入的互斥锁解锁,为什么要解锁的,拟可以想想,如果不解锁的话,那外部就没有可以对x,y的修改权,应为其他两个线程想要修改这两个值的话都需要对qclock进行枷锁。

好了线程1就这样,那之后就会运行线程2,我们看线程2的线程函数,该函数一开始也加了锁,但当线程1的pthread_cond_wait解锁之后,他就可以继续运行了,并且,在之后,它对x,y进行了修改,改好之后进行了解锁,并且调用了pthread_cond_signal通知线程1,现在可以知道了吧。这个满足的条件就是要x>y。

   现在这里有个问题,一定要在发送通知之前解锁吗?答案是肯定的,为什么,因为如果先发送通知信号给线程1的时候,pthread_cond_wait可能在线程2的解锁之前就返回,而当它返回的时候,会再次将这个所进行锁定,而这个所还没有在线程2中解锁,应次会使其在次卡住。虽然这个卡住在线程2运行到解锁处会消除,但这并不符合我们有时的需求,所以最好还是在解锁之后在发送信号。(如果看不懂的话,可以参考下面红色字体的部分!!!)

    所以可以看出为什么线程2总是在线程1之前执行完毕,线程3就更不用说了,pthread_join你们懂的!!!

 

 

为允许在线程或进程间共享数据,同步通常是必须的。常见的同步方式有:互斥锁、条件变量、读写锁、信号量。另外,对于进程间的同步,也可以通过进程间通信的方式进行同步,包括管道(无名管道、有名管道)、信号量、消息队列、共享内存、远程过程调用,当然也可以通过Socket来进行网络控制。

一.  互斥锁和条件变量是同步的基本组成部分

  互斥锁和条件变量出自Posix.1线程标准,多用来同步一个进程中各个线程。但如果将二者存放在多个进程间共享的内存区中,它们也可以用来进行进程间的同步。

1. 互斥锁用于保护临界区,以保护任何时刻只有一个线程在执行其中的代码,其大体轮廓大体如下:

  lock_the_mutex(...);

  临界区

  unlock_the_mutex(...);

  下列三个函数给一个互斥锁上锁和解锁:

  #include <pthread.h>

  int pthread_mutex_lock(pthread_mutex_t *mptr);  //若不能立刻获得锁,将阻塞在此处

  int pthread_mutex_trylock(pthread_mutex_t *mptr);  //若不能立刻获得锁,将返回EBUSY,用户可以根据此返回值做其他操作,非阻塞模式

  int pthread_mutex_unlock(pthread_mutex_t *mptr);  //释放锁

  互斥锁通常用于保护由多个线程或多个进程分享的共享数据(Share Data)

2.  条件变量,它是发送信号与等待信号。互斥锁用户上锁,条件变量则用于等待。一般来说,在一个进程/线程中调用pthread_cond_wait(..)等待某个条件的成立,此时该进程阻塞在这里,另外一个进程/线程进行某种操作,当某种条件成立时,调用pthread_cond_signal(...)来发送信号,从而使pthread_cond_wait(...)返回。此处要注意的是,这里所谈到的信号,不是系统级别的SIGXXXX信号,只是用信号这个词语更容易理解。条件变量与信号量更接近或者就可以认为是信号量。

  下列两个函数用来对条件变量进行控制:

  #include <pthread.h>

  int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr);

  int pthread_cond_signal(pthread_cond_t *cptr);

  由代码我们可以看出,条件变量的使用是需要结合锁机制的,即上面所提到的互斥锁。也就是说,一个进程/线程要等到临界区的共享数据达到某种状态时再进行某种操作,而这个状态的成立,则是由另外一个进程/线程来完成后发送信号来通知的。

  其实想一想,pthread_cond_wait函数也可以用一个while死循环来等待条件的成立,但要注意的是,使用while死循环会严重消耗CPU,而pthread_cond_wait则是采用线程睡眠的方式,它是一种等待模式,而不是一直的检查模式。

  总的来说,给条件变量发送信号的代码大体如下:

  pthread_mutex_lock(&mutex);

  设置条件为真

  

  pthread_mutex_unlock(&mutex);  

      pthread_cond_signal(&cond);  //发送信号

  等待条件并进入睡眠以等待条件变为真的代码大体如下:

  pthread_mutex_lock(&mutex); 

  while(条件为假)

    pthread_cond_wait(&cond,&mutex);  

  执行某种操作

  pthread_mutex_unlock(&mutex);

  在这里需要注意的是,pthread_cond_wait(&cond,&mutex)是一个原子操作,当它执行时,首先对mutex解锁,这样另外的线程才能得到锁来修改条件,pthread_cond_wait解锁后,再将本身的线程/进程投入睡眠,另外,当该函数返回时,会再对mutex进行加锁,这样才能“执行某种操作”后unlock锁。

 

二、 读写锁

  顾名思义,读写锁也是一种锁,他是在互斥锁的基础上进行了改进,当一个进程/线程获得写入锁时,其他的进程/线程仍然可以获得锁,只不过获得的是读取锁,因为一个进程/线程写入,不影响其他进程/线程的读操作。

 

三、 信号量

  英文:semaphore,它是一种专门用于提供不同进程间或线程间同步手段的原语。可以通过下图来理解它。

        进程A                               进程B

          \          /

     进程    \         /

        ----------------------------------------------------

     内核     \      /

              信号量

  也就是说,信号量是由内核来维护的,他独立出进程。因此可以通过它来进行同步。

  上图一般来说,是基于Posix有名信号量,可以认为它是系统中的一个特殊文件(因为在Linux中,一切都可以认为是文件),因为在进程间的通信、同步中用的比较多,如果是线程之间的同步,经常用基于Posix内存的信号量。(基于内存的信号量必须在创建时指定是否在进程间共享,有名信号量随内核有持续性,需手工删除,而基于内存的信号量具有随进程的持续性)

  对于信号量的工作原理,其实和互斥锁+条件变量相似。

  主要函数有:sem_open、sem_close、sem_unlink,这里要注意,close只是关闭信号量,但并未从系统中删除,而unlink是删除该信号量。

  sem_wait和sem_trywait函数,他们和pthread_cond_wait功能相似,都是等待某个条件的成立,sem_wait和sem_trywait的区别是,当所指定的信号量的值为0时,后者并不将调用者投入睡眠,而是立刻返回EAGAIN,即重试。

  sem_post和sem_getvalue函数,sem_post将指定的信号量加一,然后唤醒正在等待该信号量值变为正数的任意线程。sem_getvalue是用来获取当前信号量值的函数。

 

总结:

  互斥锁、条件变量、信号量三者的差别:

  (1) 互斥锁必须总是由给他上锁的线程解锁(因为此时其他线程根本得不到此锁),信号量没有这种限制:一个线程等待某个信号量,而另一个线程可以挂出该信号量

  (2)每个信号量有一个与之关联的值,挂出时+1,等待时-1,那么任何线程都可以挂出一个信号,即使没有线程在等待该信号量的值。不过对于条件变量来说,如果pthread_cond_signal之后没有任何线程阻塞在pthread_cond_wait上,那么此条件变量上的信号丢失。

  (3)在各种各样的同步技巧中,能够从信号处理程序中安全调用的唯一函数是sem_post

条件变量、信号量、互斥锁

时间: 2024-09-29 19:42:37

条件变量、信号量、互斥锁的相关文章

条件变量和互斥锁

1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<unistd.h> 5 #include<pthread.h> 6 #include<errno.h> 7 #include<iostream> 8 using namespace std; 9 10 /*提示出租车到达的条件变量*/ 11 pthread_cond_t taxi

【转载】同步和互斥的POSIX支持(互斥锁,条件变量,自旋锁)

上篇文章也蛮好,线程同步之条件变量与互斥锁的结合: http://www.cnblogs.com/charlesblc/p/6143397.html 现在有这篇文章: http://blog.csdn.net/goodluckwhh/article/details/8564319 POSIX定义了一系列同步对象用于同步和互斥.同步对象是内存中的变量属于进程中的资源,可以按照与访问数据完全相同的方式对其进行访问.默认情况下POSIX定义的这些同步对象具有进程可见性,即同步对象只对定义它的进程可见:

进程间同步(1)&mdash;&mdash;条件变量和互斥量

1. 概述 条件变量和互斥量是最基本的同步形式,总是用于同步同一个进程的各个线程间同步. 当把条件变量或互斥量放在共享内存区时,可用于进程间同步. 同样的情况还有读写锁,它们都是随进程的持续性.   2.互斥锁 互斥锁指代相互排斥,用于保护临界区.多个线程和多个进程分享的共享数据. 静态初始化:static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 动态初始化:互斥锁是动态分配的,pthread_mutex_init(&mutex);初始

条件变量与互斥量

看了很多文档,就简书说的最好. 关键词: 无竞争等待.同步机制(类似ABCABCABC).条件变量不是锁.线程阻塞.pthread_cond_wait 我理解是一个动作. 概念 线程同步的方法有多种,互斥量.信号量.条件变量.读写锁等.互斥量在允许或阻塞对临界区的访问上是很有效的,线程是在对已加锁的互斥量加锁时发生阻塞:条件变量则允许线程由于一些未达到的条件而阻塞,此处的"条件"可以由用户来定义,在访问该条件时需要加锁(互斥量),如果条件没达到,线程将阻塞在该条件上. 条件变量特别适用

linux线程间通信之条件变量和互斥量

一.条件变量定义 有的时候仅仅依靠锁住共享资源来使用它是不够的.有时候共享资源只有某些状态的时候才能够使用.比方说,某个线程如果要从堆栈中读取数据,那么如果栈中没有数据就必须等待数据被压栈.这种情况下的同步使用互斥锁是不够的.另一种同步的方式--条件变量,就可以使用在这种情况下.条件变量(Condition Variable)是线程间的一种同步机制,提供给两个线程协同完成任务的一种方法,使用条件变量可以以原子方式阻塞线程,直到某个特定条件为真为止.条件变量的测试一般是用互斥量来保护的,用来确保每

Linux互斥锁、条件变量和信号量

Linux互斥锁.条件变量和信号量  来自http://kongweile.iteye.com/blog/1155490 博客分类: Linux sem_init:初始化信号量sem_t,初始化的时候可以指定信号量的初始值,以及是否可以在多进程间共享.sem_wait:一直阻塞等待直到信号量>0.sem_timedwait:阻塞等待若干时间直到信号量>0.sem_post:使信号量加1.sem_destroy:释放信号量.和sem_init对应. 进行多线程编程,最应该注意的就是那些共享的数据

互斥锁和条件变量的结合使用

互斥锁一个明显的缺点是他只有两种状态:锁定和非锁定.而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,他常和互斥锁一起使用.使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化.一旦其他的某个线程改变了条件变量,他将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程.这些线程将重新锁定互斥锁并重新测试条件是否满足.一般说来,条件变量被用来进行线承间的同步. 对于条件锁,通常配合一个互斥锁一起使用,以防止多个线程同时请求pt

深入理解Solaris内核中互斥锁(mutex)与条件变量(condvar)之协同工作原理

在Solaris上写内核模块总是会用到互斥锁(mutex)与条件变量(condvar), 光阴荏苒日月如梭弹指一挥间,Solaris的大船说沉就要沉了,此刻心情不是太好(Orz).每次被年轻的有才华的同事们(比如Letty同学)问起mutex和cv怎么协同工作的,我总是不能给出一个非常清晰的解释.直到今天,看了cv_wait()的源代码之后,我终于可以给他们一个清楚明白的回答了. Solaris的源码无法被公开粘贴出来,幸好还有OpenSolaris的继承者illumos. 先贴cv_wait(

信号量与条件变量的区别

注意信号量与条件变量的区别 信号量内容可见:http://www.cnblogs.com/charlesblc/p/6142868.html 信号量.共享内存,以及消息队列等System V IPC三剑客主要关注进程间通信: 而条件变量.互斥锁,主要关注线程间通信. 下面内容参考:http://blog.chinaunix.net/uid-27164517-id-3282242.html pthread_cond_wait指的是条件变量,总和一个互斥锁结合使用.在调用pthread_cond_w

使用互斥量和条件变量实现线程同步控制

管程(monitor)说明 在并发编程中,管程(monitor)是一个同步构件,管程实现了同一时间点,最多只有一个线程可以执行管程的某个子程序.与那些通过修改数据结构实现互斥访问的并发程序设计相比,管程的实现很大程度上简化了程序设计. 管程可以确保一次只有一个进程执行管程中的程序,因此程序员不需要显式地编写同步代码,但是如果需要就某些特定条件上的同步,则需要定义一些条件结构(condition variable)来实现,并且对条件变量的操作仅有wait()和signal(),如下: condit