我们何时应该使用互斥锁?什么时候应该使用信号量?
答案 0 :(得分:82)
以下是我记得何时使用 -
<强>信号量:强> 当你(线程)想睡觉直到某个其他线程告诉你醒来时使用信号量。信号量'down'发生在一个线程(生产者)中,信号量'up'(相同的信号量)发生在另一个线程(消费者)中 例如:在生产者 - 消费者问题中,生产者想睡觉直到至少一个缓冲槽为空 - 只有消费者线程可以告诉缓冲槽何时为空。
<强>互斥:强> 当(线程)想要执行不应由任何其他线程同时执行的代码时,请使用互斥锁。 Mutex'down'发生在一个线程中,而互斥''up'必须在稍后的同一个线程中发生。 例如:如果要从全局链表中删除节点,则在删除节点时,不希望其他线程使用指针。当您获取互斥锁并且正在忙于删除节点时,如果另一个线程尝试获取相同的互斥锁,它将被置于休眠状态,直到您释放该互斥锁。
<强>螺旋锁:强> 当你真的想使用互斥锁时使用螺旋锁,但不允许你的线程睡眠。 例如:OS内核中的中断处理程序绝对不能休眠。如果是,系统将冻结/崩溃。如果需要从中断处理程序向全局共享链表插入节点,请获取自旋锁 - 插入节点 - 释放自旋锁。
答案 1 :(得分:55)
互斥锁是一个互斥对象,与信号量类似,但它一次只允许一个锁定器,其所有权限制可能比信号量更严格。
可以认为它等同于正常的计数信号量(计数为1),并且要求它只能由锁定它的同一个线程释放(a)。 / p>
另一方面,信号量具有任意计数,并且可以同时锁定多个储物柜。它可能没有要求它由声称它的同一个线程发布(但是,如果没有,你必须仔细跟踪当前对它负责的人,就像分配的内存一样)。
因此,如果您有多个资源实例(例如三个磁带驱动器),您可以使用一个计数为3的信号量。请注意,这并不能告诉您这些磁带驱动器中的哪一个,只是你有一定的数字。
对于信号量,单个锁定器可以锁定资源的多个实例,例如磁带到磁带的副本。如果您有一个资源(比如一个您不想破坏的内存位置),则更适合使用互斥锁。
等效操作是:
Counting semaphore Mutual exclusion semaphore
-------------------------- --------------------------
Claim/decrease (P) Lock
Release/increase (V) Unlock
除了:如果您曾经想过用于声明和发布信号量的奇怪信件,那是因为发明者是荷兰人。探测器te verlagen意味着尝试减少,而verhogen意味着增加。
(a) ...或者它可以被认为是与信号量完全不同的东西,鉴于它们几乎总是不同的用途,它们可能更安全。
答案 2 :(得分:44)
了解互斥锁 不是 计数为1的信号量非常重要!
这就是二进制信号量(它们实际上是计数器1的信号量)之类的原因。
Mutex和Binary-Semaphore之间的区别是所有权原则:
任务获取互斥锁,因此也必须由同一任务释放。 这样就可以解决二进制信号量的几个问题(Accidential release,递归死锁和优先级倒置)。
警告:我写道“让它成为可能”,这些问题是否以及如何解决取决于操作系统的实现。
因为必须通过相同的任务释放互斥锁,所以对任务的同步不是很好。但如果与条件变量结合使用,您将获得用于构建各种ipc原语的非常强大的构建块。
所以我的建议是:如果你使用干净的互斥锁和条件变量(比如使用POSIX pthreads),请使用这些。
只有在信号量与您尝试解决的问题完全吻合时才使用信号量,不要尝试构建其他基元(例如,信号量中的rw-locks,使用互斥锁和条件变量)
有很多误解互斥量和信号量。到目前为止,我发现的最佳解释是在这篇3部分文章中:
Mutex vs. Semaphores – Part 1: Semaphores
Mutex vs. Semaphores – Part 2: The Mutex
Mutex vs. Semaphores – Part 3 (final part): Mutual Exclusion Problems
答案 3 :(得分:13)
虽然@opaxdiablo的答案是完全正确的,但我想指出两者的使用场景是完全不同的。互斥锁用于保护代码部分不会同时运行,信号量用于一个线程以指示另一个线程运行。
/* Task 1 */
pthread_mutex_lock(mutex_thing);
// Safely use shared resource
pthread_mutex_unlock(mutex_thing);
/* Task 2 */
pthread_mutex_lock(mutex_thing);
// Safely use shared resource
pthread_mutex_lock(mutex_thing);
信号量场景不同:
/* Task 1 - Producer */
sema_post(&sem); // Send the signal
/* Task 2 - Consumer */
sema_wait(&sem); // Wait for signal
有关详细说明,请参阅http://www.netrino.com/node/202
答案 4 :(得分:9)
参见“厕所示例” - http://pheatt.emporia.edu/courses/2010/cs557f10/hand07/Mutex%20vs_%20Semaphore.htm:
<强>互斥:强>
是厕所的钥匙。一个人可以拥有钥匙 - 占用厕所 - 当时。完成后,该人将(释放)密钥提供给队列中的下一个人。
官方说:“互斥锁通常用于序列化对一部分重入代码的访问,这些代码不能由多个线程同时执行。互斥对象只允许一个线程进入受控部分,强制其他线程尝试获得访问该部分的权限,等待第一个线程退出该部分。“ 参考:Symbian开发人员库
(互斥锁实际上是一个值为1的信号量。)
<强>信号量:强>
是免费相同的厕所钥匙的数量。例如,我们说有四个带有相同锁和钥匙的马桶。信号量计数 - 键数 - 在开始时设置为4(所有四个厕所都是免费的),然后计数值随着人们的进入而减少。如果所有厕所都已满,即。没有剩余的自由键,信号量计数为0.现在,当eq。一个人离开厕所,信号量增加到1(一个免费密钥),并送给队列中的下一个人。
正式:“信号量将共享资源的同时用户数量限制为最大数量。线程可以请求访问资源(减少信号量),并且可以发信号通知他们已经完成使用资源(递增信号)。” 参考:Symbian开发人员库
答案 5 :(得分:7)
尽量不发声,但不能帮助自己。
您的问题应该是互斥和信号量之间的区别是什么? 更准确的问题应该是,'互斥和信号量之间的关系是什么?'
(我会添加这个问题,但我百分百肯定有些过分热心的主持人会将其作为重复关闭,而不理解差异和关系之间的区别。)
在对象术语中,我们可以观察到:
观察.1信号量包含互斥量
观察.2互斥量不是信号量,信号量不是互斥量。
有些信号量就像是互斥量一样,称为二进制信号量,但它们不是互斥量。
有一种称为信号的特殊成分(posix使用condition_variable作为该名称),需要用互斥锁制作信号量。 将其视为通知源。如果两个或多个线程订阅了相同的notification-source,则可以将消息发送给ONE或ALL,以便唤醒。
可能有一个或多个与信号量相关联的计数器,这些计数器由互斥锁保护。对于信号量最简单的场景,有一个计数器可以是0或1。
这就像季风雨中的混乱一样。
计数器可以是0或1的信号量不是互斥量。
Mutex有两个状态(0,1)和一个所有权(任务)。 信号量有一个互斥锁,一些计数器和一个条件变量。
现在,运用你的想象力,计数器的使用和何时发出信号的每种组合都可以成为一种信号量。
值为0或1的单个计数器,当值变为1时发出信号然后解锁其中一个等待信号的人==二进制信号
单个计数器,其值为0到N,当值变为小于N时发出信号,并在值为N ==计数信号量时锁定/等待
值为0到N的单个计数器,当值变为N时发出信号,当值小于N =阻隔信号量时锁定/等待(如果他们不调用它,那么他们应该。)
现在问你的问题,何时使用什么。 (或者更正确的问题版本.3何时使用互斥量以及何时使用二进制信号量,因为没有与非二进制信号量进行比较。) 使用互斥时 1.你想要一个二进制信号量不提供的自定义行为,例如自旋锁或快速锁或递归锁。 您通常可以使用属性自定义互斥锁,但自定义信号量只不过是编写新的信号量。 2.你想要轻量级或更快的原语
使用信号量,当你想要的东西完全由它提供时。
如果您不了解二进制信号量的实现提供的内容,那么恕我直言,请使用互斥锁。
最后读了一本书,而不仅仅依靠SO。
答案 6 :(得分:5)
我认为问题应该是互斥信号和二进制信号量之间的区别。
Mutex =它是一种所有权锁定机制,只有获得锁定的线程才能释放锁定。
二进制信号量=它更像是一种信号机制,任何其他更高优先级的线程,如果想要信号并采取锁定。
答案 7 :(得分:3)
Mutex是为了保护共享资源
信号量就是发送线程。
互斥:
想象一下,有一些卖票。我们可以模拟一个案例,许多人同时购买门票:每个人都是买票的线程。显然,我们需要使用互斥锁来保护票证,因为它是共享资源。
信号灯:
想象一下,我们需要进行如下计算:
c = a + b;
此外,我们需要一个函数geta()
来计算a
,一个函数getb()
来计算b
和一个函数getc()
来进行计算{{ 1}}。
显然,除非c = a + b
和c = a + b
已完成,否则我们无法执行geta()
。
如果三个函数是三个线程,我们需要调度三个线程。
getb()
在信号量的帮助下,上面的代码可以确保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);
无法完成工作,直至t3
和t1
完成工作。
总之,信号量是使线程作为逻辑顺序执行,而互斥是保护共享资源。
因此,即使有些人总是说互斥锁是一个初始值为1的特殊信号量,它们也不是一回事。您也可以这样说,但请注意它们在不同情况下使用。即使你能做到这一点,也不要互相替换。
答案 8 :(得分:1)
正如所指出的,计数为1的信号量与“二进制”信号量相同,它与互斥量相同。
我见过的主要信息是,计数大于1的信号量是生产者/消费者情况,其中你有一个固定大小的队列。
然后你有两个信号量。第一个信号量最初设置为队列中的项目数,第二个信号量设置为0.生产者对第一个信号量执行P操作,添加到队列中。并在第二个上进行V操作。消费者对第二个信号量执行P操作,从队列中删除,然后对第一个信号量执行V操作。
通过这种方式,只要填充队列,生产者就会被阻止,并且只要队列为空,就会阻止使用者。
答案 9 :(得分:1)
互斥锁是信号量的特例。信号量允许多个线程进入临界区。创建信号量时,您可以定义在关键部分中允许线程的方式。当然,您的代码必须能够处理对此关键部分的多次访问。
答案 10 :(得分:1)
以上所有答案质量都很好,但这只是为了记住。名称 Mutex 源自 互斥 因此你有动机将互斥锁视为两个之间的相互排斥,就像一次只有一个,如果我拥有它,你只能在我释放后才能拥有它。另一方面,这种情况不存在信号量就像一个交通信号(信号量也意味着)。
答案 11 :(得分:-1)
二进制信号量和互斥量不同。从操作系统的角度来看,二进制信号量和计数信号量的实现方式相同,并且二进制信号量的值可以为0或1。
Mutex ->只能用于一个关键部分的互斥,并且只能互斥。
信号量->可用于解决各种问题。二进制信号量可以用于信令,也可以解决互斥问题。初始化为 0 时,它解决了信号发送问题;初始化为 1 时,它解决了互斥问题。 / p>
当资源数量更多并且需要同步时,我们可以使用计数信号量。
在我的博客中,我详细讨论了这些主题。
https://designpatterns-oo-cplusplus.blogspot.com/2015/07/synchronization-primitives-mutex-and.html