二进制信号量和互斥量之间的区别


819

二进制信号量和互斥量之间是否有任何区别,或者它们基本相同?


11
它们在语义上是相同的,但是在实践中您会注意到奇怪的差异(尤其是在Windows上)。
Michael Foukarakis,2009年

5
@Michael Foukarakis:奇怪的区别是什么?
菲利普

2
我认为这不是正确的表达方式。互斥锁还支持所有权,有时还支持重入。在Windows中就是这种情况。此外,Windows中的信号量是在Event对象之上实现的,但是,我不确定这样做的实际含义。
Michael Foukarakis 2010年


2
@philipxy很好地在“ m”的位置隐藏了“ rn”。
Mooncrater

Answers:


691

他们不是一回事。它们用于不同目的!
虽然两种类型的信号量都具有完整/空状态并使用相同的API,但它们的用法却大不相同。

互斥信号灯
互斥信号灯用于保护共享资源(数据结构,文件等)。

Mutex信号量由执行该任务的任务“拥有”。如果任务B尝试给任务A当前持有的互斥锁赋值,则任务B的调用将返回错误并失败。

互斥对象始终使用以下顺序:

  -SemTake
  -关键部分
  -SemGive

这是一个简单的示例:

  线程A线程B
   服用Mutex
     存取资料
     ...以Mutex <==将阻止
     ...
   授予Mutex访问数据<==解除阻止
                                  ...
                                给互斥体

Binary Semaphore
Binary Semaphore解决了一个完全不同的问题:

  • 等待任务B等待某事发生(例如,传感器跳闸)。
  • 传感器跳闸并运行中断服务程序。它需要通知行程任务。
  • 任务B应该运行,并为传感器跳闸采取适当的措施。然后回到等待状态。

   Task A                      Task B
   ...                         Take BinSemaphore   <== wait for something
   Do Something Noteworthy
   Give BinSemaphore           do something    <== unblocks

请注意,对于二进制信号量,B可以接收信号量,而A可以给出信号量。
同样,二进制信号量不能保护资源免受访问。发出信号量和采取信号量的行为从根本上是分离的。
对于相同的二进制信号量来说,给予和接受相同的任务通常没有多大意义。


12
互斥量不是比二进制信号量好吗?因为如果有人释放了他实际上没有持有的锁,那是没有意义的。
Pacerier 2011年

111
他们有不同的目的。互斥锁是对资源的独占访问。应使用二进制信号量进行同步(即“嘿,有人!这发生了!”)。二进制“给予者”只是通知“接受者”的任何人他们在等待什么。
Benoit

5
@Pacerier您在混淆目的。互斥锁旨在保护关键区域。您是正确的,使用二进制信号量没有任何意义。我将更新答案以解释每个目的。
Benoit 2012年

4
@Benoit那么,我们可以说互斥用于原子性,而二进制信号量则用于订购透视图,因为任务B将等待任务A发出信号以释放信号,从而确保对数据结构上的操作进行订购?
阿比

1
@abhi这是查看Mutex的好方法。但是,根据操作系统的不同,您可能有多个收件人在等待二进制信号量。在这种情况下,只有一个客户端将获得二进制sem。其他人将等待下一个。接收顺序是已知的还是保证的?取决于操作系统。
Benoit 2014年

446
  • 一个互斥体只能被释放的是已经获得它的线程
  • 二进制信号量,可以发信号通知任何线程(或进程)。

因此,信号量更适合于一些同步问题,例如生产者-消费者。

在Windows上,二进制信号量比互斥体更像事件对象。


34
Mutex can be released only by thread that had acquired it-我刚刚尝试了一个简单的基于pthread_mutex的程序,一个线程可以解锁锁定在主线程中的互斥锁
daisy 2012年

15
@ warl0ck按照pthread_mutex_lock的手册页linux.die.net/man/3/pthread_mutex_lock:“如果互斥锁类型为PTHREAD_MUTEX_ERRORCHECK,则应提供错误检查。...如果线程试图解锁它具有的互斥锁未锁定或未锁定的互斥锁,将返回错误。”
阿米特

47
@ warl0ck请参阅stackoverflow.com/a/5492499/385064'Pthreads具有3种不同类型的互斥锁:快速互斥锁,递归互斥锁和错误检查互斥锁。您使用了快速互斥锁,由于性能原因,该互斥锁不会检查此错误。如果在Linux上使用错误检查互斥锁,您会发现您得到了预期的结果。
FrostNovaZzz

1
在我们的代码中,我们也将互斥锁用于同步目的。锁定互斥锁的线程再次尝试锁定互斥锁,然后进入阻塞状态。我们看到的是我们能够从另一个线程解锁此互锁锁。两者之间的同步。我们仅使用posix标准。因此,互斥量和二进制信号量之间的主要区别似乎很模糊。
achoora

1
@achoora我同意将信号量专门用于同步是错误的。实际上,所有互斥量,二进制信号量,屏障,管道都是用于同步的不同模式。从设计的角度来看,互斥锁更像是状态模式,其中状态选择的算法可以更改状态。二进制信号量更像策略模式,其中外部算法可以更改状态,并最终选择要运行的算法/策略。
舒瓦

442

厕所的例子很有趣:

互斥体:

是上厕所的钥匙。当时,一个人可以拥有钥匙-上厕所。完成后,此人将密钥释​​放(释放)给队列中的下一个人。

正式地:“ Mutexe通常用于序列化不能由多个线程同时执行的可重入代码段的访问。互斥对象仅允许一个线程进入受控段,从而迫使其他试图获取访问权限的线程该部分等待,直到第一个线程从该部分退出。” 参考:Symbian开发人员库

(互斥量实际上是值为1的信号量。)

信号:

是免费的相同马桶钥匙的数量。例如,假设我们有四个具有相同锁和钥匙的卫生间。信号量计数(键的计数)在开始时设置为4(所有四个洗手间都是免费的),然后计数值随着人们的进来而递减。如果所有洗手间都已满,即。没有剩余的可用键,信号量为0。一个人离开洗手间,信号量增加到1(一个自由键),并交给队列中的下一个人。

正式地:“信号量将共享资源的并发用户数限制为最大数量。线程可以请求访问该资源(减少信号量),并且可以发出信号,表明他们已经使用完资源(增加信号量)。 ” 参考:Symbian开发人员库


233
...但这是关于互斥与计数信号量的关系。有人问了有关二进制的问题。
Roman Nikitchenko

24
虽然大卫的说法是正确的,但这并不是所提问题的答案。Mladen Jankovic答案是对所提问题的答案,其中重点是区分“二进制信号量”与“互斥量”。
Ajeet Ganga

13
不幸的是,这个错误的答案比@Benoit的最佳答案拥有更多的投票
NeonGlow 2013年

6
这个答案是误导性的,应该只与Binary Semaphore进行比较。
Hemanth 2014年

3
这也演示了使用计数信号量来保护共享资源的问题:如果密钥确实相同,并且使用密钥对厕所进行了解锁,并且没有其他机制可以分配小隔间使用量,则:(1)第一种用户解锁,进入并开始使用第一个隔间。(2)下一个用户解锁,进入并开始使用第一个隔间...
Technophile 2015年

151

关于该主题的不错的文章:

从第2部分开始:

互斥锁与二进制信号量的原理相似,但有一个明显的区别:所有权原理。所有权是一个简单的概念,当一个任务锁定(获取)一个互斥锁时,它只能解锁(释放)它。如果任务试图解锁一个尚未锁定的互斥锁(因此不拥有),则会遇到错误情况,最重要的是,互斥锁不会解锁。如果互斥对象没有所有权,那么与它所调用的内容无关,它不是互斥体。


感谢您的链接,那里的解释很好。该链接已更改:feabhas.com/blog/2009/09/...(使用<Prev和Next>导航到另外两篇文章。
亚伦H.

@亚伦(Aaron)我已修复链接
法官Maygarden 2010年

注意-缺乏所有权还会阻止操作系统解决优先级倒置问题。因此,对于生产者/消费者体系结构,我通常使用条件变量而不是信号量。
kgriffs 2011年

1
+1优秀文章链接。最好的文章,解释了“什么-IT-是”旗语和互斥和“是什么,它-做” computing.llnl.gov/tutorials/pthreads我用这篇文章作为我身后的场景参考,这在技术上确实说明一切互斥体/条件句和其他构建在其顶部的结构(如信号量/屏障/阅读器/书写器),但在任何地方都没有明确和简洁的结构所面临的问题。简而言之,它是参考。:)
Ajeet Ganga

比其他答案更容易理解。
BinaryTreeee '18

101

由于以上答案都不能消除混乱,因此以下是我的困惑。

严格来说,互斥锁是一种锁定机制用于同步对资源的访问。只有一个任务(可以是基于OS抽象的线程或进程)可以获取互斥量。这意味着将有与互斥锁关联的所有权,只有所有者才能释放锁(互斥锁)。

信号量是信号机制(“我做完了,您可以进行”这种信号)。例如,如果您正在手机上收听歌曲(假设是一项任务),并且您的朋友同时打给您,则将触发一个中断,中断服务程序(ISR)将在此信号通知呼叫处理任务唤醒。

资料来源:http : //www.geeksforgeeks.org/mutex-vs-semaphore/


42

它们的同步语义非常不同:

  • 互斥锁允许序列化对给定资源的访问,即多个线程一次等待一个锁,并且如前所述,该线程拥有该锁直到完成:只有这个特定线程才能解锁它。
  • 二进制信号量是一个值为0和1的计数器,一个任务在其上阻塞,直到任何任务执行sem_post 为止。信号量通告资源可用,并且它提供了一种机制,等待信号通知资源可用。

因此,可以将互斥锁看作是从一个任务传递到另一个任务的令牌,而将信号量看作是流量红灯(表示有人可以继续进行操作)。


23

从理论上讲,它们在语义上没有什么不同。您可以使用信号量实现互斥量,反之亦然(请参见此处)的示例)。实际上,实现方式有所不同,它们提供的服务略有不同。

实际的区别(就围绕它们的系统服务而言)是,互斥锁的实现旨在成为一种更轻量级的同步机制。在甲骨文中,互斥锁称为闩锁,信号量称为等待

在最低级别上,他们使用某种原子测试和设置机制。这将读取内存位置的当前值,计算某种条件的值,并在一条不能中断的指令中写出该位置的值。这意味着您可以获取一个互斥锁并进行测试,以查看是否有人在您之前拥有它。

一个典型的互斥量实现有一个进程或线程执行“测试并设置”指令并评估是否有其他东西设置了互斥量。这里的关键点是,与调度程序之间没有交互,因此我们不知道(也不在乎)谁设置了锁定。然后,我们要么放弃时间片,然后在重新计划任务时重试时间片,要么执行自旋锁。自旋锁是一种类似以下的算法:

Count down from 5000:
     i. Execute the test-and-set instruction
    ii. If the mutex is clear, we have acquired it in the previous instruction 
        so we can exit the loop
   iii. When we get to zero, give up our time slice.

完成执行受保护的代码后(称为关键部分))后,我们只需将互斥锁值设置为零或任何表示“清除”的值即可。如果有多个任务试图获取互斥锁,则在释放互斥锁之后碰巧计划的下一个任务将可以访问该资源。通常,您将使用互斥锁来控制同步资源,其中仅在非常短的时间内才需要独占访问,通常可以对共享数据结构进行更新。

信号量是一个同步的数据结构(通常使用互斥锁),它具有一个计数,并且某些系统调用包装程序与调度程序进行交互的深度比互斥锁库要深一些。信号量会增加和减少,并用于阻止任务,直到准备好其他东西为止。有关此示例,请参阅生产者/消费者问题。信号量被初始化为某个值-二进制信号量只是信号量被初始化为1的特例。发给信号量具有唤醒等待过程的效果。

基本的信号量算法如下所示:

(somewhere in the program startup)
Initialise the semaphore to its start-up value.

Acquiring a semaphore
   i. (synchronised) Attempt to decrement the semaphore value
  ii. If the value would be less than zero, put the task on the tail of the list of tasks waiting on the semaphore and give up the time slice.

Posting a semaphore
   i. (synchronised) Increment the semaphore value
  ii. If the value is greater or equal to the amount requested in the post at the front of the queue, take that task off the queue and make it runnable.  
 iii. Repeat (ii) for all tasks until the posted value is exhausted or there are no more tasks waiting.

在二进制信号量的情况下,两者之间的主要实际区别是围绕实际数据结构的系统服务的性质。

编辑:正如evan正确指出的那样,自旋锁会降低单处理器计算机的速度。您只能在多处理器盒上使用自旋锁,因为在单处理器上,持有互斥锁的进程永远不会在其他任务运行时将其重置。自旋锁仅在多处理器体系结构上有用。


1
我认为通过自旋锁实现互斥锁不是常见的做法。在Uni-proc机器上,这对于性能绝对是可怕的。
Evan Teran

通常,您只会在多处理器系统上使用自旋锁。
ConcernedOfTunbridgeWells,

即使在SMP上,经过几次旋转后,您仍会退回到OS辅助的睡眠/唤醒状态。(例如,futex存在Linux 系统调用来辅助低延迟用户空间互斥体/信号量实现。en.wikipedia.org / wiki / Futex)在无争用的快速路径中,或者如果资源很快可用,则您永远不会有开销系统调用。但是您的忙碌等待(旋转)花费的时间不会超过几微秒。当然,调整自旋循环退避和等待的参数取决于硬件和工作负载,但是标准库通常具有合理的选择。
彼得·科德斯

19

尽管互斥量和信号量被用作同步原语,但它们之间还是有很大的区别。对于互斥锁,只有锁定或获取了互斥锁的线程才能对其进行解锁。对于信号量,等待信号量的线程可以由其他线程发出信号。一些操作系统支持在进程之间使用互斥量和信号量。通常,用法是在共享内存中创建。


“可以通过不同的线程发出信号”是一个例子。
Myanju

15

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

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

二进制信号量:它基于信令等待和信号。等待将“ s”值减小一,通常将“ s”值初始化为值“ 1”,信号将“ s”值增大一。如果“ s”值为1表示没有人使用临界区,而值为0则表示正在使用临界区。假设线程T2使用的是关键部分,则遵循以下步骤。T2:

  1. wait(s)//最初的值是在调用wait之后为1,其值减小了1,即0
  2. 使用关键部分
  3. 信号//现在s的值增加并且变为1

Mutex和Binary信号量之间的主要区别在于Mutext中,如果线程锁定了关键部分,则它必须解锁关键部分,没有其他线程可以解锁它,但是对于Binary信号量,如果一个线程使用wait(s)函数锁定了关键部分,则value的s变为“ 0”,直到“ s”的值变为1之前没有人可以访问它,但假设其他线程调用信号,则“ s”的值变为1,它允许其他函数使用临界区。因此,在二进制信号量线程中没有所有权。


12

在Windows上,互斥量和二进制信号量之间有两个区别:

  1. 互斥锁只能由具有所有权的线程释放,即先前称为Wait函数的线程(或在创建该线程时拥有所有权的线程)。信号量可以由任何线程释放。

  2. 线程可以在互斥对象上反复调用wait函数而不会阻塞。但是,如果您在二进制信号量上调用了两次wait函数而没有释放它们之间的信号量,则该线程将阻塞。


4
好答案。在#2中,您描述的是递归互斥锁-并非所有互斥锁都必须是递归的。例如,cs.wustl.edu /〜schmidt / ACE.FAQ.html#Q14

10

显然,您可以使用互斥锁将数据锁定在一个线程中,同时另一个线程可以访问该数据。假设您刚刚打电话lock()并且正在访问数据。这意味着您不希望任何其他线程(或相同线程代码的另一个实例)访问由相同互斥锁锁定的相同数据。也就是说,如果在不同的线程实例上执行的是相同的线程代码,则将其锁定,然后lock()应该阻止那里的控制流。这适用于使用不同线程代码的线程,该线程代码也正在访问相同的数据,并且也被相同的互斥锁锁定。在这种情况下,您仍处于访问数据的过程中,并且可能要花15秒钟才能达到互斥锁解锁(这样,在互斥锁中被阻塞的另一个线程将解除阻塞,并使控件能够访问数据)。您是否不惜一切代价允许另一个线程仅解锁同一个互斥锁,然后又允许互斥锁中已在等待(阻塞)的线程解除阻塞并访问数据?希望你明白我在这里说的话吗?根据一致同意的通用定义!,

  • 使用“互斥体”不会发生这种情况。没有其他线程可以解锁您的线程中的锁
  • 使用“二进制信号量”会发生这种情况。任何其他线程都可以解锁您线程中的锁

因此,如果您非常在意使用二进制信号量而不是互斥量,那么在“确定”锁定和解锁范围时应格外小心。我的意思是,碰到每个锁的每个控制流都应该打一个解锁调用,也不应有任何“首次解锁”,而应该始终是“首次锁定”。



9

神话:

一对文章说,“二进制信号量和互斥量是相同的”或“值为1的信号量是互斥量”,但基本区别是Mutex只能由已获取它的线程释放,而您可以从任何其他线程发出信号量

关键点:

•一个线程可以获取多个锁(Mutex)。

•互斥锁只能是递归互斥锁才能多次锁定,此处互斥锁的锁定和解锁应该相同

•如果已经锁定了互斥锁的线程再次尝试锁定该互斥锁,它将进入该互斥锁的等待列表,这将导致死锁。

•二进制信号量和互斥量相似但不相同。

•由于与相关的保护协议,Mutex的运行成本很高。

•互斥锁的主要目的是实现原子访问或锁定资源


8

互斥控制接入到单个共享资源。它提供操作来获取()访问该资源和释放()在完成后。

一个信号量控制对资源的共享池。它向Wait()提供操作,直到池中的资源之一变为可用为止,然后Signal()时将其返回给池。

当信号量保护的资源数量大于1时,称为计数信号量。当它控制一个资源时,它称为布尔信号量。布尔信号量等效于互斥量。

因此,信号量是比互斥对象更高级别的抽象。互斥量可以使用信号量来实现,但不能相反。


6

修改后的问题是-“ Linux”中的互斥锁和“二进制”信号灯有什么区别?

答案:以下是区别– i)范围–互斥锁的范围在创建它的进程地址空间内,并用于线程同步。信号量可以在整个过程空间中使用,因此可以用于进程间同步。

ii)Mutex比信号量轻巧且速度更快。Futex甚至更快。

iii)互斥量可以被同一线程多次成功获取,但前提是它必须释放相同的次数。尝试获取的其他线程将阻塞。而在信号量的情况下,如果相同的过程尝试再次获取它,则会阻塞,因为它只能被获取一次。


我)错了。ii)来源?iii)这取决于。
curiousguy

6

Binary Semaphore和Mutex之间的区别:所有权: 甚至可以从非当前所有者发出信号(张贴)。这意味着,尽管您不是所有者,但您可以从任何其他线程中进行发布。

信号量是一个正在使用的公共财产,可以由非所有者线程简单地发布。请用粗体字母标记出此差异,这很重要。


5

Mutex可以阻止关键区域,而Semaphore则可以实现计数。



4

除了互斥对象具有所有者这一事实之外,还可以针对不同的用法优化这两个对象。互斥对象只能保留很短的时间。违反此规定可能会导致性能下降和调度不公。例如,即使另一个线程已被阻塞,正在运行的线程也可能被允许获取互斥量。信号量可以提供更多的公平性,或者可以使用多个条件变量来强制公平性。


在哪些特定情况下可以保证信号量的公平性而不是互斥量的公平性?
curiousguy

1
POSIX有哪个线程应该被唤醒的具体要求sem_post()SCHED_FIFOSCHED_RR(这些不是默认):最高优先级的线程,如果有多个具有相同的优先级,已等待时间最长的线程。即使对于正常调度,OpenSolaris在某种程度上也会遵循此FIFO规则。对于glibc和FreeBSD,解锁简单的互斥锁(即不是优先级保护或优先级继承)和发布信号量基本上是相同的,将对象标记为未锁定,然后,如果可能有等待线程,则调用内核唤醒该对象。
jilles 2011年

4

在Windows中,区别如下。 MUTEX:成功执行等待的过程必须执行一个信号,反之亦然。二进制SEMAPHORES:不同的进程可以对信号量执行等待信号操作。


4

虽然可以将二进制信号量用作互斥锁,但是互斥锁是一种更特定的用例,因为只有锁定互斥锁的过程才可以对其进行解锁。此所有权约束使得可以针对以下情况提供保护:

  • 意外释放
  • 递归死锁
  • 任务死亡僵局

这些约束并不总是存在,因为它们会降低速度。在开发代码期间,您可以临时启用这些检查。

例如,您可以在互斥锁中启用错误检查属性。EDEADLK如果您尝试两次锁定同一EPERM互斥锁,并且解锁了不是您自己的互斥锁,则会返回错误检查互斥锁的错误。

pthread_mutex_t mutex;
pthread_mutexattr_t attr;
pthread_mutexattr_init (&attr);
pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
pthread_mutex_init (&mutex, &attr);

初始化后,我们可以将这些检查放入我们的代码中,如下所示:

if(pthread_mutex_unlock(&mutex)==EPERM)
 printf("Unlock failed:Mutex not owned by this thread\n");

4

在浏览完上述帖子后,这个概念对我很清楚。但是还有一些挥之不去的问题。所以,我写了这段小代码。

当我们尝试不使用信号灯时,它就会通过。但是,当您尝试不使用互斥体而给出互斥体时,它将失败。我在Windows平台上对此进行了测试。启用USE_MUTEX以使用MUTEX运行相同的代码。

#include <stdio.h>
#include <windows.h>
#define xUSE_MUTEX 1
#define MAX_SEM_COUNT 1

DWORD WINAPI Thread_no_1( LPVOID lpParam );
DWORD WINAPI Thread_no_2( LPVOID lpParam );

HANDLE Handle_Of_Thread_1 = 0;
HANDLE Handle_Of_Thread_2 = 0;
int Data_Of_Thread_1 = 1;
int Data_Of_Thread_2 = 2;
HANDLE ghMutex = NULL;
HANDLE ghSemaphore = NULL;


int main(void)
{

#ifdef USE_MUTEX
    ghMutex = CreateMutex( NULL, FALSE, NULL);
    if (ghMutex  == NULL) 
    {
        printf("CreateMutex error: %d\n", GetLastError());
        return 1;
    }
#else
    // Create a semaphore with initial and max counts of MAX_SEM_COUNT
    ghSemaphore = CreateSemaphore(NULL,MAX_SEM_COUNT,MAX_SEM_COUNT,NULL);
    if (ghSemaphore == NULL) 
    {
        printf("CreateSemaphore error: %d\n", GetLastError());
        return 1;
    }
#endif
    // Create thread 1.
    Handle_Of_Thread_1 = CreateThread( NULL, 0,Thread_no_1, &Data_Of_Thread_1, 0, NULL);  
    if ( Handle_Of_Thread_1 == NULL)
    {
        printf("Create first thread problem \n");
        return 1;
    }

    /* sleep for 5 seconds **/
    Sleep(5 * 1000);

    /*Create thread 2 */
    Handle_Of_Thread_2 = CreateThread( NULL, 0,Thread_no_2, &Data_Of_Thread_2, 0, NULL);  
    if ( Handle_Of_Thread_2 == NULL)
    {
        printf("Create second thread problem \n");
        return 1;
    }

    // Sleep for 20 seconds
    Sleep(20 * 1000);

    printf("Out of the program \n");
    return 0;
}


int my_critical_section_code(HANDLE thread_handle)
{

#ifdef USE_MUTEX
    if(thread_handle == Handle_Of_Thread_1)
    {
        /* get the lock */
        WaitForSingleObject(ghMutex, INFINITE);
        printf("Thread 1 holding the mutex \n");
    }
#else
    /* get the semaphore */
    if(thread_handle == Handle_Of_Thread_1)
    {
        WaitForSingleObject(ghSemaphore, INFINITE);
        printf("Thread 1 holding semaphore \n");
    }
#endif

    if(thread_handle == Handle_Of_Thread_1)
    {
        /* sleep for 10 seconds */
        Sleep(10 * 1000);
#ifdef USE_MUTEX
        printf("Thread 1 about to release mutex \n");
#else
        printf("Thread 1 about to release semaphore \n");
#endif
    }
    else
    {
        /* sleep for 3 secconds */
        Sleep(3 * 1000);
    }

#ifdef USE_MUTEX
    /* release the lock*/
    if(!ReleaseMutex(ghMutex))
    {
        printf("Release Mutex error in thread %d: error # %d\n", (thread_handle == Handle_Of_Thread_1 ? 1:2),GetLastError());
    }
#else
    if (!ReleaseSemaphore(ghSemaphore,1,NULL) )      
    {
        printf("ReleaseSemaphore error in thread %d: error # %d\n",(thread_handle == Handle_Of_Thread_1 ? 1:2), GetLastError());
    }
#endif

    return 0;
}

DWORD WINAPI Thread_no_1( LPVOID lpParam ) 
{ 
    my_critical_section_code(Handle_Of_Thread_1);
    return 0;
}


DWORD WINAPI Thread_no_2( LPVOID lpParam ) 
{
    my_critical_section_code(Handle_Of_Thread_2);
    return 0;
}

即使信号量从未拥有该资源,它也可以让您发出信号“使用资源完成操作”这一事实使我认为,在信号量的情况下,拥有和信号之间存在非常松散的耦合。


如果您阅读其他答案,很显然,“所有权”的概念仅对互斥量有意义,而对信号量则无意义。信号量可以用于类似线程的事情,让其他线程知道已经完成了对数据块的处理。结果可供阅读。
彼得·科德斯

2

Mutex用于保护敏感代码和数据,信号量用于同步。您也可以与保护敏感代码一起实际使用,但是可能存在通过操作V释放其他线程的保护的风险。双信号量和互斥量之间的区别是所有权。例如,在洗手间,互斥量就像一个人可以进入马桶并锁上门,直到有人下车,其他人才能进入;双信号量就像一个人可以进入厕所并锁上门,但是其他人可以通过要求管理员打开门来进入,这很荒谬。


2

互斥体

互斥锁通常用于序列化对不能由多个线程同时执行的可重入代码段的访问。互斥对象仅允许一个线程进入受控部分,从而迫使试图访问该部分的其他线程要等到第一个线程退出该部分后再使用。正确使用互斥对象是为了保护共享资源,可能会有危险。意外的副作用。以不同优先级运行并通过互斥体进行协调的任何两个RTOS任务都会为优先级倒置创造机会。Mutex在用户空间中工作

信号

信号量是一种信号机制。信号量将共享资源的同时用户数限制为最大数量。线程可以请求对资源的访问(减少信号量),并且可以发出信号,表明它们已经完成了对资源的使用(增加信号量)。它允许多个线程访问共享资源。信号量的正确用法是用于将信号从一个任务发送到另一任务,也可以将信号量用于从中断服务程序(ISR)信号发送到任务。发信号量信号是一种无阻塞的RTOS行为,因此具有ISR安全性。由于此技术消除了在任务级别禁用中断的容易出错的需求,因此可以在内核空间中使用


1

答案可能取决于目标操作系统。例如,至少一个我熟悉的RTOS实现将允许对单个OS互斥锁进行多个顺序的“获取”操作,只要它们都来自同一线程上下文中即可。在允许另一个线程获取互斥体之前,必须用相等数量的puts替换多个get。 这不同于二进制信号量,二进制信号量一次仅允许一次获取,而与线程上下文无关。

这种互斥量背后的想法是,通过仅允许单个上下文一次修改数据来保护对象。即使线程获取了互斥锁,然后调用了进一步修改对象的函数(并使其自身的操作获取/放置了保护器互斥锁),这些操作仍应是安全的,因为它们都是在单个线程下发生的。

{
    mutexGet();  // Other threads can no longer get the mutex.

    // Make changes to the protected object.
    // ...

    objectModify();  // Also gets/puts the mutex.  Only allowed from this thread context.

    // Make more changes to the protected object.
    // ...

    mutexPut();  // Finally allows other threads to get the mutex.
}

当然,使用此功能时,必须确保单个线程内的所有访问确实是安全的!

我不确定这种方法有多普遍,或者它是否适用于我所熟悉的系统之外。有关此类互斥锁的示例,请参见ThreadX RTOS。


2
您正在谈论的这种互斥锁称为“递归互斥锁”,应避免使用,因为它们很慢并且容易导致不良的设计:(请参阅David Butenhof:zaval.org/resources/library/butenhof1.html
gaspard

同意 在这个特定的OS上,我要使用互斥锁服务,在这里我想清楚地表明代码是“互斥”的,而不是引用计数,但是我不使用递归功能,以免难看。在问题的上下文中,这仍然是“互斥体”和“二进制信号量”之间的重要区别。
Casey Barker 2010年

1

与信号量不同,互斥体具有所有权。尽管在互斥锁范围内的任何线程都可以获得解锁的互斥锁并锁定对同一关键代码段的访问,但是只有锁定了互斥锁的线程才应对其进行解锁


什么是所有权?您的意思是获取互斥量的上下文只能取消获取它。
拉乌普2012年

1

正如这里的许多人所提到的,互斥锁用于保护关键代码段(AKA关键段)。您将在同一线程中获取该互斥锁(锁定),进入关键段并释放互斥锁(解锁)

在使用信号量时,您可以让一个线程在信号量(例如线程A)上等待,直到另一个线程(例如线程B)完成任何任务,然后为线程A设置信号量以停止等待并继续其任务。


1

最佳解决方案

唯一的区别是

1.互斥锁->锁定和解锁处于锁定互斥锁的线程的所有权之下。

2.Semaphore->没有所有权,即;如果一个线程调用semwait,则其他线程可以调用sempost来删除锁。


1

MUTEX

直到最近,内核中唯一的睡眠锁是信号灯。大多数使用信号量的用户都实例化了一个计数为1的信号量,并将其视为互斥锁(自旋锁的休眠版本)。不幸的是,信号量是相当通用的,没有施加任何使用限制。这使它们对于在晦涩的情况下(例如内核和用户空间之间的复杂舞蹈)管理独占访问很有用。但这也意味着更难进行锁定,而且缺少强制性规则使得不可能进行任何类型的自动调试或约束性强制执行。为了寻求更简单的睡眠锁,内核开发人员引入了互斥锁。是的,正如您现在所习惯的,这是一个令人困惑的名称。让我们澄清一下。术语“互斥体”是一个通用名称,指的是任何强制实施互斥的睡眠锁,例如使用次数为1的信号量。在最近的Linux内核中,专有名词“互斥锁”现在也是实现互斥的特定类型的睡眠锁。也就是说,互斥锁是互斥锁。

互斥量的简单性和效率来自于信号量所需要的其他限制。与根据Dijkstra的原始设计实现最基本行为的信号量不同,互斥量具有更严格,更狭窄的用例:n一次仅一个任务可以容纳互斥量。也就是说,互斥量的使用次数始终为1。

  1. 锁定互斥锁的人必须将其解锁。也就是说,您不能在一个上下文中锁定互斥锁,然后在另一上下文中解锁互斥锁。这意味着互斥锁不适用于内核和用户空间之间更复杂的同步。但是,大多数用例都是从同一上下文中干净地锁定和解锁。
  2. 不允许递归锁定和解锁。也就是说,您不能递归获取相同的互斥锁,也无法解锁未锁定的互斥锁。
  3. 持有互斥锁时进程无法退出。
  4. 即使使用mutex_trylock(),也无法通过中断处理程序或下半部分获取互斥量。
  5. 互斥锁只能通过官方API进行管理:必须通过本节中介绍的方法对其进行初始化,并且不能对其进行复制,手动初始化或重新初始化。

[1] Linux内核开发,第三版Robert Love


1

我认为这里的大多数答案令人困惑,尤其是那些说互斥锁只能通过持有互斥锁的进程来释放,而信号量可以通过ay进程来发出信号的人。上面的行在信号量方面有点模糊。要理解,我们应该知道有两种信号量,一种叫做计数信号量,另一种叫做二进制信号量。在计数时,信号量处理对n个资源的访问,其中可以在使用前定义n。每个信号量都有一个count变量,该变量保留使用中的资源数量的计数,最初将其设置为n。每个希望使用资源的进程都对信号量执行wait()操作(从而减少计数)。进程释放资源时,它将执行release()操作(增加计数)。当计数变为0时,所有资源都在使用中。之后,该进程等待,直到计数超过0。这是仅捕获拥有资源的进程可以增加计数的捕获,没有其他进程可以增加计数,只有拥有资源的进程可以增加计数,并且该进程等待信号量再次检查,并在看到可用资源时再次减少计数。因此,就二进制信号量而言,只有持有该信号量的进程才能增加计数,并且计数保持为零,直到它停止使用该信号量并增加计数为止,其他进程才有机会访问该信号量。现在这里是捕获,只有持有资源的进程可以增加计数,没有其他进程可以增加计数,只有持有资源的进程可以增加计数,等待信号量的进程再次检查,并在看到资源可用时再进行计数再次减少计数。因此,就二进制信号量而言,只有持有该信号量的进程才能增加计数,并且计数保持为零,直到它停止使用该信号量并增加计数为止,其他进程才有机会访问该信号量。现在这里是捕获,仅持有资源的进程可以增加计数,而其他进程则不能增加计数,只有持有资源的进程可以增加计数,等待信号量的进程再次检查,并在看到资源可用时再进行计数再次减少计数。因此,就二进制信号量而言,只有持有该信号量的进程才能增加计数,并且计数保持为零,直到它停止使用该信号量并增加计数,其他进程才有机会访问该信号量。

二进制信号量和互斥量之间的主要区别在于,信号量是一种信号传递机制,互斥量是一种锁定机制,但是二进制信号量似乎像互斥量一样起作用,但会造成混淆,但是两者都是适用于不同类型工作的不同概念。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.