信号量与互斥锁的区别

之前遇到一个问题,信号量和互斥锁的区别是什么。一时忘了思考,今天才想到这个问题,翻阅知乎和stackoverflow,理解了之后做简单整理

一、定义

mutex,互斥锁,用于序列化对一部分可重入代码的访问,这些代码不能由多个线程同时执行

semaphore,信号量,将共享资源的并发用户数限制为最大数量

二、使用

Mutex:假设我们有关键部分线程T1想要访问它然后它遵循以下步骤:

  1. 使用关键部分
  2. 开锁

二进制信号量:它基于信令等待和信号工作。等待(s)将“s”值减少一个通常“s”值用值“1”初始化,信号(s)将“s”值增加1。如果“s”值为1表示没有人使用临界区,则值为0表示临界区正在使用中。假设线程T2正在使用临界区,那么它遵循以下步骤:

  1. wait(s)//最初s值在调用之后等于它的值减1,即0
  2. 使用关键部分
  3. signal(s)//现在s值增加,变为1

三、具体分析(引自知乎)

mutex,典型的例子就是买票: 票是共享资源,现在有两个线程同时过来买票。如果你不用mutex在线程里把票锁住,那么就可能出现“把同一张票卖给两个不同的人(线程)”的情况。 我想这个不需要多解释了。

一般人不明白semaphore和mutex的区别,根本原因是不知道semaphore的用途。 semaphore的用途,一句话:调度线程。 有的人用semaphore也可以把上面例子中的票“保护"起来以防止共享资源冲突,必须承认这是可行的,但是semaphore不是让你用来做这个的;如果你要做这件事,请用mutex。

在网上、包括stackoverflow等著名论坛上,有一个流传很广的厕所例子: mutex是一个厕所一把钥匙,谁抢上钥匙谁用厕所,谁没抢上谁就等着;semaphore是多个同样厕所多把同样的钥匙 ---- 只要你能拿到一把钥匙,你就可以随便找一个空着的厕所进去。 事实上,这个例子对初学者、特别是刚刚学过mutex的初学者来说非常糟糕 ----- 我第一次读到这个例子的第一反应是:semaphore是线程池???所以,请务必忘记这个例子。 另外,有人也会说:mutex就是semaphore的value等于1的情况。 这句话不能说不对,但是对于初学者来说,请先把这句话视为错误;等你将来彻底融会贯通这部分知识了,你才能真正理解上面这句话到底是什么意思。总之请务必记住:mutex干的活儿和semaphore干的活儿不要混起来。

在这里,我模拟一个最典型的使用semaphore的场景: a源自一个线程,b源自另一个线程,计算c = a + b也是一个线程。(即一共三个线程)

显然,第三个线程必须等第一、二个线程执行完毕它才能执行。 在这个时候,我们就需要调度线程了:让第一、二个线程执行完毕后,再执行第三个线程。 此时,就需要用semaphore了。
 
int a, b, c;
void geta()
{
    a = calculatea();
    semaphore_increase();
}

void getb()
{
    b = calculateb();
    semaphore_increase();
}


void getc()
{
    semaphore_decrease();
    semaphore_decrease();
    c = a + b;
}

t1 = thread_create(geta);
t2 = thread_create(getb);
t3 = thread_create(getc);
thread_join(t3);

// semaphore的机制我在这里就不讲了,百度一下你就知道。
// semaphore_increase对应sem_post
// semaphore_decrease对应sem_wait
这就是semaphore最典型的用法。 说白了,调度线程,就是:一些线程生产(increase)同时另一些线程消费(decrease),semaphore可以让生产和消费保持合乎逻辑的执行顺序。 而线程池是程序员根据具体的硬件水平和不同的设计需求、为了达到最佳的运行效果而避免反复新建和释放线程同时对同一时刻启动的线程数量的限制,这完全是两码事。 比如如果你要计算z = a + b +...+ x + y ...的结果,同时每个加数都是一个线程,那么计算z的线程和每个加数的线程之间的逻辑顺序是通过semaphore来调度的;而至于你运行该程序的时候到底要允许最多同时启动几个线程,则是用线程池来实现的。

简而言之,锁是服务于共享资源的;而semaphore是服务于多个线程间的执行的逻辑顺序的

请回头看那个让大家忘记的厕所例子。我之所以让大家忘记这个例子,是因为如果你从这个角度去学习semaphore的话,一定会和mutex混为一谈。semaphore的本质就是调度线程 ---- 在充分理解了这个概念后,我们再看这个例子。

semaphore是通过一个值来实现线程的调度的,因此借助这种机制,我们也可以实现对线程数量的限制。而当我们把线程数量限制为1时,你会发现:共享资源受到了保护 ------ 任意时刻只有一个线程在运行,因此共享资源当然等效于受到了保护。但是我要再提醒一下,如果你要对共享资源进行保护,请用mutex;到底应该用条件锁还是用semaphore,请务必想清楚。通过semaphore来实现对共享资源的保护的确可行但是是对semaphore的一种错用

只要你能搞清楚锁、条件锁和semaphore为什么而生、或者说它们是面对什么样的设计需求、为了解决什么样类型的问题才出现的,你自然就不会把他们混淆起来。

作者:二律背反 链接:https://www.zhihu.com/question/47704079/answer/135859188 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 
posted @ 2018-10-20 16:52  小时候挺菜  阅读(2425)  评论(0编辑  收藏  举报