信号量vs互斥锁(Semaphore vs Mutex)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了信号量vs互斥锁(Semaphore vs Mutex)相关的知识,希望对你有一定的参考价值。
参考技术A 信号量是一个被线程共享的非负变量。信号量是一个发信号的机制。一个等待一个信号量的线程可以被其他线程通知(signal)。这个机制通过 wait 和 signal 两个原子操作(atomic operations)来实现进程同步。一个信号量要么允许访问资源,要么不允许访问资源。二者只能选其一。而具体是哪一种,则要看设置。
详情可参考《 信号量:二进位信号量和计数信号量 》这篇文章。
互斥锁其实是一个对象。Mutex的全称是Mutual Exclusion Object,也就是互斥锁是一个互斥对象。它是一种特殊的二进位信号量(binary semaphore),用来控制访问共享区域资源。它包括一个优先级继承机制,以避免扩展的优先级反转问题。它允许当前优先级较高的任务在阻塞状态下维持的时间尽可能的少。然而,优先级继承并不能完全避免优先级反转,而只会最小化其影响。
对于单个缓冲区(single buffer),我们可以将4kb缓冲区分成四个1kb缓冲区。信号量可以与这四个缓冲区相关联。这允许用户和生产者同时处理不同的缓冲区。
互斥锁用于提供互斥,它使得拥有钥匙(key or mutex)的生产者才能访问资源。只要生产者占用了缓冲区(buffer),用户必须等待,反之亦然。在互斥锁的机制中,整块缓冲区始终只能提供给一个线程访问。
下面列举信号量的优点:
下面列举互斥锁的优点:
下面列举信号量的缺点:
下面列举互斥锁的缺点:
信号量与互斥锁的区别
之前遇到一个问题,信号量和互斥锁的区别是什么。一时忘了思考,今天才想到这个问题,翻阅知乎和stackoverflow,理解了之后做简单整理
一、定义
mutex,互斥锁,用于序列化对一部分可重入代码的访问,这些代码不能由多个线程同时执行
semaphore,信号量,将共享资源的并发用户数限制为最大数量
二、使用
Mutex:假设我们有关键部分线程T1想要访问它然后它遵循以下步骤:
- 锁
- 使用关键部分
- 开锁
二进制信号量:它基于信令等待和信号工作。等待(s)将“s”值减少一个通常“s”值用值“1”初始化,信号(s)将“s”值增加1。如果“s”值为1表示没有人使用临界区,则值为0表示临界区正在使用中。假设线程T2正在使用临界区,那么它遵循以下步骤:
- wait(s)//最初s值在调用之后等于它的值减1,即0
- 使用关键部分
- 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是服务于多个线程间的执行的逻辑顺序的。
请回头看那个让大家忘记的厕所例子。我之所以让大家忘记这个例子,是因为如果你从这个角度去学习semaphore的话,一定会和mutex混为一谈。semaphore的本质就是调度线程 ---- 在充分理解了这个概念后,我们再看这个例子。
semaphore是通过一个值来实现线程的调度的,因此借助这种机制,我们也可以实现对线程数量的限制。而当我们把线程数量限制为1时,你会发现:共享资源受到了保护 ------ 任意时刻只有一个线程在运行,因此共享资源当然等效于受到了保护。但是我要再提醒一下,如果你要对共享资源进行保护,请用mutex;到底应该用条件锁还是用semaphore,请务必想清楚。通过semaphore来实现对共享资源的保护的确可行但是是对semaphore的一种错用。
只要你能搞清楚锁、条件锁和semaphore为什么而生、或者说它们是面对什么样的设计需求、为了解决什么样类型的问题才出现的,你自然就不会把他们混淆起来。