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

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

答案

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

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

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

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

- SemTake
  - Critical Section
  - SemGive

这是一个简单的示例:

Thread A                     Thread B
   Take Mutex
     access data
     ...                        Take Mutex  <== Will block
     ...
   Give Mutex                     access data  <== Unblocks
                                  ...
                                Give Mutex

二进制信号量
二进制信号量解决了一个完全不同的问题:

  • 等待任务 B 等待某事发生(例如,传感器跳闸)。
  • 传感器跳闸并运行中断服务程序。它需要通知行程任务。
  • 任务 B 应运行并为传感器跳闸采取适当的措施。然后回到等待状态。
Task A                      Task B
   ...                         Take BinSemaphore   <== wait for something
   Do Something Noteworthy
   Give BinSemaphore           do something    <== unblocks

请注意,对于二进制信号量,B 可以选择信号量,而 A 可以提供信号量。
同样,二进制信号量不能保护资源免受访问。发出信号和采取信号量的行为从根本上是分离的。
通常,对于同一任务来说,对相同的二进制信号量进行赠与取几乎没有什么意义。

厕所的例子很有趣:

互斥体:

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

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

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

信号:

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

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

  • 互斥锁只能由已获取 互斥锁 的线程释放。
  • 二进制信号量可以由任何线程 (或进程)发出信号。

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

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

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

从第 2 部分开始:

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

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

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

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

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

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

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

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

  • 顾名思义, 互斥锁用于序列化对不能由多个线程同时执行的可重入代码段的访问。

  • 根据定义, 信号量会将共享资源的同时用户数限制为最大数量

  • 信号量可以是互斥量,但互斥量永远不能是信号量。这仅意味着二进制信号量可以用作互斥量,但是互斥量永远不能表现出信号量的功能。

  • 信号量和 Mutex(至少是最新的内核)本质上都是非递归的。
  • 没有人拥有信号灯,而 Mutex 是拥有的,而所有者则对此负有责任。从调试的角度来看,这是一个重要的区别。
  • 如果是互斥锁,则拥有互斥锁的线程负责释放它。但是,在信号量的情况下,不需要此条件。任何其他线程都可以通过使用 smps(function.e_ot)发出信号以释放信号量

  • 对开发人员而言,另一个重要的区别是信号量是系统范围的,除非以其他方式进行清理,否则它们将以文件形式保留在文件系统中。互斥锁是整个进程的,并在进程退出时自动清除。

  • 信号量的性质使得可以在同步相关和不相关的进程以及线程之间使用它们。 Mutex 仅可用于线程之间以及最多相关进程之间的同步(最新内核的 pthread 实现具有允许在相关进程之间使用 Mutex 的功能)。
  • 根据内核文档,与信号量相比,互斥量更轻。这意味着与使用 Mutex 的程序相比,使用信号量的程序具有更高的内存占用量。
  • 从使用角度来看,与信号量相比,互斥体的语义更简单。

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

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

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

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

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

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

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,它允许其他函数使用临界区。因此,在二进制信号量线程中没有所有权。