我们何时应该使用互斥锁,何时应该使用信号量

时间:2010-10-28 05:00:27

标签: linux multithreading synchronization mutex semaphore

我们何时应该使用互斥锁?什么时候应该使用信号量?

12 个答案:

答案 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)和一个所有权(任务)。 信号量有一个互斥锁,一些计数器和一个条件变量。

现在,运用你的想象力,计数器的使用和何时发出信号的每种组合都可以成为一种信号量。

  1. 值为0或1的单个计数器,当值变为1时发出信号然后解锁其中一个等待信号的人==二进制信号

  2. 单个计数器,其值为0到N,当值变为小于N时发出信号,并在值为N ==计数信号量时锁定/等待

  3. 值为0到N的单个计数器,当值变为N时发出信号,当值小于N =阻隔信号量时锁定/等待(如果他们不调用它,那么他们应该。)

  4. 现在问你的问题,何时使用什么。 (或者更正确的问题版本.3何时使用互斥量以及何时使用二进制信号量,因为没有与非二进制信号量进行比较。) 使用互斥时 1.你想要一个二进制信号量不提供的自定义行为,例如自旋锁或快速锁或递归锁。 您通常可以使用属性自定义互斥锁,但自定义信号量只不过是编写新的信号量。 2.你想要轻量级或更快的原语

    使用信号量,当你想要的东西完全由它提供时。

    如果您不了解二进制信号量的实现提供的内容,那么恕我直言,请使用互斥锁。

    最后读了一本书,而不仅仅依靠SO。

答案 6 :(得分:5)

我认为问题应该是互斥信号和二进制信号量之间的区别。

Mutex =它是一种所有权锁定机制,只有获得锁定的线程才能释放锁定。

二进制信号量=它更像是一种信号机制,任何其他更高优先级的线程,如果想要信号并采取锁定。

答案 7 :(得分:3)

Mutex是为了保护共享资源 信号量就是发送线程。

互斥:
想象一下,有一些卖票。我们可以模拟一个案例,许多人同时购买门票:每个人都是买票的线程。显然,我们需要使用互斥锁来保护票证,因为它是共享资源。


信号灯:
想象一下,我们需要进行如下计算:

c = a + b;

此外,我们需要一个函数geta()来计算a,一个函数getb()来计算b和一个函数getc()来进行计算{{ 1}}。

显然,除非c = a + bc = 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); 无法完成工作,直至t3t1完成工作。

总之,信号量是使线程作为逻辑顺序执行,而互斥是保护共享资源。
因此,即使有些人总是说互斥锁是一个初始值为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