信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在哪里)。而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这个资源。比如对全局变量的访问,有时要加锁,操作完了,在解锁。有的时候锁和信号量会同时使用的”
也就是说,信号量不一定是锁定某一个资源,而是流程上的概念,比如:有A,B两个线程,B线程要等A线程完成某一任务以后再进行自己下面的步骤,这个任务并不一定是锁定某一资源,还可以是进行一些计算或者数据处理之类。而线程互斥量则是“锁住某一资源”的概念,在锁定期间内,其他线程无法对被保护的数据进行操作。在有些情况下两者可以互换。

两者之间的区别:

作用域
信号量: 进程间或线程间(linux仅线程间)
互斥锁: 线程间

上锁时
信号量: 只要信号量的value大于0,其他线程就可以sem_wait成功,成功后信号量的value减一。若value值不大于0,则sem_wait阻塞,直到sem_post释放后value值加一
互斥锁: 只要被锁住,其他任何线程都不可以访问被保护的资源

成功后否则就阻塞

以下是信号灯(量)的一些概念:

信号灯与互斥锁和条件变量的主要不同在于”灯”的概念,灯亮则意味着资源可用,灯灭则意味着不可用。如果说后两中同步方式侧重于”等待”操作,即资源不可用的话,信号灯机制则侧重于点灯,即告知资源可用;没有等待线程的解锁或激发条件都是没有意义的,而没有等待灯亮的线程的点灯操作则有效,且能保持灯亮状态。当然,这样的操作原语也意味着更多的开销。

信号灯的应用除了灯亮/灯灭这种二元灯以外,也可以采用大于1的灯数,以表示资源数大于1,这时可以称之为多元灯。

1. 创建和 注销

POSIX信号灯标准定义了有名信号灯和无名信号灯两种,但LinuxThreads的实现仅有无名灯,同时有名灯除了总是可用于多进程之间以外,在使用上与无名灯并没有很大的区别,因此下面仅就无名灯进行讨论。

int sem_init(sem_t *sem, int pshared, unsigned int value)
这是创建信号灯的API,其中value为信号灯的初值,pshared表示是否为多进程共享而不仅仅是用于一个进程。LinuxThreads没有实现多进程共享信号灯,因此所有非0值的pshared输入都将使sem_init()返回-1,且置errno为ENOSYS。初始化好的信号灯由sem变量表征,用于以下点灯、灭灯操作。

int sem_destroy(sem_t * sem)
被注销的信号灯sem要求已没有线程在等待该信号灯,否则返回-1,且置errno为EBUSY。除此之外,LinuxThreads的信号灯 注销函数不做其他动作。

2. 点灯和灭灯

int sem_post(sem_t * sem)

点灯操作将信号灯值原子地加1,表示增加一个可访问的资源。

int sem_wait(sem_t * sem)
int sem_trywait(sem_t * sem)

sem_wait()为等待灯亮操作,等待灯亮(信号灯值大于0),然后将信号灯原子地减1,并返回。sem_trywait()为sem_wait()的非阻塞版,如果信号灯计数大于0,则原子地减1并返回0,否则立即返回-1,errno置为EAGAIN。

3. 获取灯值

int sem_getvalue(sem_t * sem, int * sval)

读取sem中的灯计数,存于*sval中,并返回0。

4. 其他

sem_wait()被实现为取消点,而且在支持原子”比较且交换”指令的体系结构上,sem_post()是唯一能用于异步信号处理函数的POSIX异步信号 安全的API。

----------------------------
线程同步:何时互斥锁不够,还需要条件变量?

假设有共享的资源sum,与之相关联的mutex 是lock_s.假设每个线程对sum的操作很简单的,与sum的状态无关,比如只是sum++.那么只用mutex足够了.程序员只要确保每个线程操作前,取得lock,然后sum++,再unlock即可.每个线程的代码将像这样
add()
{
pthread_mutex_lock(lock_s);
sum++;
pthread_mutex_unlock(lock_s);
}

  如果操作比较复杂,假设线程t0,t1,t2的操作是sum++,而线程t3则是在sum到达100的时候,打印出一条信息,并对sum清零. 这种情况下,如果只用mutex, 则t3需要一个循环,每个循环里先取得lock_s,然后检查sum的状态,如果sum>=100,则打印并清零,然后unlock.如果sum& amp; lt;100,则unlock,并sleep()本线程合适的一段时间.

 这个时候,t0,t1,t2的代码不变,t3的代码如下
print()
{
while (1)
{
pthread_mutex_lock(lock_s);
if(sum<100)
{
printf(“sum reach 100!”);
pthread_mutex_unlock(lock_s);
}
else
{
pthread_mutex_unlock(lock_s);
my_thread_sleep(100);
return OK;
}
}
}

这种办法有两个问题
1) sum在大多数情况下不会到达100,那么对t3的代码来说,大多数情况下,走的是else分支,只是lock和unlock,然后sleep().这浪费了CPU处理时间.
2) 为了节省CPU处理时间,t3会在探测到sum没到达100的时候sleep()一段时间.这样却又带来另外一个问题,亦即t3响应速度下降.可能在sum到达200的时候,t4才会醒过来.
3) 这样,程序员在设置sleep()时间的时候陷入两难境地,设置得太短了节省不了资源,太长了又降低响应速度.真是难办啊!

  这个时候,condition variable内裤外穿,从天而降,拯救了焦头烂额的你.

  你首先定义一个condition variable.
pthread_cond_t cond_sum_ready=PTHREAD_COND_INITIALIZER;

  t0,t1,t2的代码只要后面加两行,像这样
add()
{
pthread_mutex_lock(lock_s);
sum++;
pthread_mutex_unlock(lock_s);
if(sum>=100)
pthread_cond_signal(&cond_sum_ready);
}
而t3的代码则是
print
{
pthread_mutex_lock(lock_s);
while(sum<100)
pthread_cond_wait(&cond_sum_ready, &lock_s);
printf(“sum is over 100!”);
sum=0;
pthread_mutex_unlock(lock_s);
return OK;
}

注意两点:
1) 在thread_cond_wait()之前,必须先lock相关联的mutex, 因为假如目标条件未满足,pthread_cond_wait()实际上会unlock该mutex, 然后block,在目标条件满足后再重新lock该mutex, 然后返回.
2) 为什么是while(sum<100),而不是if(sum<100) ?这是因为在pthread_cond_signal()和pthread_cond_wait()返回之间,有时间差,假设在这个时间差内,还有另外一个线程t4又把sum减少到100以下了,那么t3在pthread_cond_wait()返回之后,显然应该再检查一遍sum的大小.这就是用 while的用意

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

 #include <iostream>
#include <pthread.h>
using namespace std; pthread_cond_t qready = PTHREAD_COND_INITIALIZER; //初始构造条件变量
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER; //初始构造锁
pthread_t tid1,tid2,tid3; int x = ;
int y = ; void *thrd_1(void *arg)
{
pthread_mutex_lock(&qlock);
while(x<y)
{
pthread_cond_wait(&qready,&qlock);
}
pthread_mutex_unlock(&qlock);
cout<<""<<endl;
sleep();
} void *thrd_2(void *arg)
{
pthread_mutex_lock(&qlock);
x = ;
y = ;
cout<<"has change x and y"<<endl; pthread_mutex_unlock(&qlock);
if(x > y)
{
pthread_cond_signal(&qready);
}
cout<<""<<endl;
} void *thrd_3(void *arg)
{
pthread_join(tid1,NULL);
cout<<""<<endl;
} int main(int argc,char **argv)
{
int err;
err = pthread_create(&tid1,NULL,thrd_1,NULL);
if(err != )
{
cout<<"pthread 1 create error"<<endl;
}
err = pthread_create(&tid2,NULL,thrd_2,NULL);
if(err != )
{
cout<<"pthread 2 create error"<<endl;
}
err = pthread_create(&tid3,NULL,thrd_3,NULL);
if(err != )
{
cout<<"pthread 3 create error"<<endl;
}
while()
{
sleep();
}
return ; }

可以看到,创建了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你们懂的!!!

总结:

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

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

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

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

【C】——信号量 互斥锁 条件变量的区别的更多相关文章

  1. C++11 多线程同步 互斥锁 条件变量

    在多线程程序中,线程同步(多个线程访问一个资源保证顺序)是一个非常重要的问题,Linux下常见的线程同步的方法有下面几种: 互斥锁 条件变量 信号量 这篇博客只介绍互斥量和条件变量的使用. 互斥锁和条 ...

  2. 并发编程---互斥锁---互斥锁与join的区别

    互斥锁 互斥锁:就是把多个进程并发,修改成一块共享数据的操作变成串行,保证是一个一个来修改的. 缺点:效率低,加锁过程复杂 优点:增加了安全性 from multiprocessing import ...

  3. [转]Posix-- 互斥锁 条件变量 信号量

    这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第三篇将向您讲述线程同步. 互斥锁 尽管在Posix Thread中同样可以使用IPC的信号量 ...

  4. [openmp]使用嵌套互斥锁锁定变量

    本文出自:http://www.cnblogs.com/svitter 转载请注明出处. 如果有一个线程必须要同时加锁两次,只能用嵌套型锁函数 函数名称 描述 void omp_init_nest_l ...

  5. 嵌入式开发之hi3519---进程线程间的同步和互斥,条件变量、信号了、互斥锁等

    sem_post 最安全 sem  有序,会卡顿 阻塞 mutex  无序,不能同步 http://blog.chinaunix.net/uid-20671208-id-4935154.html ht ...

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

    上篇文章也蛮好,线程同步之条件变量与互斥锁的结合: http://www.cnblogs.com/charlesblc/p/6143397.html   现在有这篇文章: http://blog.cs ...

  7. C++ 并发编程之互斥锁和条件变量的性能比较

    介绍 本文以最简单生产者消费者模型,通过运行程序,观察该进程的cpu使用率,来对比使用互斥锁 和 互斥锁+条件变量的性能比较. 本例子的生产者消费者模型,1个生产者,5个消费者. 生产者线程往队列里放 ...

  8. linux条件变量使用和与信号量的区别

    近来在项目中用到条件变量和信号量做同步时,这一块一直都有了解,但也一直没有总结,这次总结一下,给大家提供点参考,也给自己留点纪念. 首先,关于信号量和条件变量的概念可以自行查看APUE,我这直接把AP ...

  9. c++11中的线程、锁和条件变量

    void func(int i, double d, const string& s) { cout << i << ", " << d ...

随机推荐

  1. extjs4学习-02-导入相关文件

    在WebContent下创建extjs4目录. 将extjs项目文件中的resource文件夹和ext-all.js.ext-all.js.ext-all-debug.js文件拷贝进去.

  2. mac 上面安装jdk 1.6

    下载地址 https://support.apple.com/kb/dl1572?locale=zh_CN orcale 支持mac的最低版本是1.7

  3. 【嵌入式】arm-linux-gcc/ld/objcopy/objdump参数概述

    arm-linux-gcc -o 只激活预处理,编译,和汇编,也就是他只把程序做成obj文件   -Wall 指定产生全部的警告信息   -O2 编译器对程序提供的编译优化选项,在编译的时候使用该选项 ...

  4. 玩转Bootstrap(JS插件篇)-第1章 模态弹出框 :1-1导入JavaScript插件

    导入JavaScript插件 Bootstrap除了包含丰富的Web组件之外,如前面介绍的下拉菜单.按钮组.导航.分页等.他还包括一些JavaScript的插件. Bootstrap的JavaScri ...

  5. input type= file 如何更改自定义的样式

    input { @include wh(24px,22px);//sass 宽高 @include pa(0,0); //绝对定位 top:0:left:0: opacity: 0; //透明度: o ...

  6. Linux源码分析之:malloc、free

    之前写代码的时候一直有个疑问,malloc申请内存的时候指定了内存大小,但是free的时候却只指定要释放的内存地址,那么free是如何知道它要释放的内存空间大小呢? 源码之前,了无秘密,下面就从源码来 ...

  7. Mac系统安装Lua

    1.下载最新版的lua-5.2.3 请点击,然后解压 2. 运行“终端”进入到该文件夹下,主要是cd [文件夹名] 3.在“终端”输入    make macosx   (回车) 4.在“终端”输入  ...

  8. Python排序dict之list数组

    两种办法: 其一lambda表达式: ctx['data'] = sorted(ctx['data'], key=lambda k: k['asrtime'], reverse=True) 其二利用o ...

  9. 【转】一件有趣的事:我用 Python 爬了爬自己的微信朋友

    偶然了解到 Python 里的 itchat 包,它已经完成了 wechat 的个人账号 API 接口,使爬取个人微信信息更加方便. 于是乎玩心一起,打算爬一下自己的微信. 步骤核心: 网页启动not ...

  10. C#数组 添加元素

    例1: string[] a = new string[] { "1", "2", "3" }; 给a追加一个 "4" ...