什么时候应该使用互斥锁,什么时候应该使用信号量


Answers:


92

这是我记得何时使用什么的方式-

信号: 当您(线程)想要睡觉时使用信号量,直到其他线程告诉您醒来。信号量“下降”发生在一个线程(生产者)中,信号量“上升”(对于相同的信号量)发生在另一个线程(消费者)中,例如:在生产者-消费者问题中,生产者想要休眠直到至少一个缓冲区为空-仅使用者线程可以知道缓冲区插槽何时为空。

互斥锁: 当您(线程)想要执行不应同时由任何其他线程执行的代码时,请使用互斥锁。互斥锁“向下”发生在一个线程中,互斥锁“向上” 必须稍后在同一线程中发生。例如:如果要从全局链表中删除一个节点,则在删除该节点时,您不希望其他线程随指针乱码。当您获取一个互斥锁并忙于删除节点时,如果另一个线程试图获取相同的互斥锁,则它将进入休眠状态,直到您释放该互斥锁。

自旋锁: 使用一个自旋锁,当你真的想用一个互斥体,但你的线程不允许睡觉。例如:OS内核中的中断处理程序永远不能休眠。如果这样做,系统将冻结/崩溃。如果需要从中断处理程序中将节点插入到全局共享的链表中,请获取自旋锁-插入节点-释放自旋锁。


1
添加到:信号量和互斥量是提供同步的两种方法。信号量可能与信令(例如,生产者和消费者的问题场景)更为相关,而互斥量则可能与一次允许访问一个资源(访问共享资源的多个请求,但一次仅授予一个请求)更多相关。[不错的文章:geeksforgeeks.org/mutex-vs-semaphore/]
parasrish

57

互斥锁是互斥对象,类似于信号量,但一次只能允许一个储物柜,并且其所有权限制可能比信号量更严格。

可以认为它等同于普通计数信号量(计数为1),并且要求它只能由锁定它的同一线程释放(a)

另一方面,信号量具有任意数量,并且可以同时被那么多储物柜锁定。而且它可能并不需要由声明它的线程来释放它(但是,如果没有,则必须仔细地跟踪谁当前对此负责,就像分配的内存一样)。

因此,如果您有多个资源实例(例如三个磁带驱动器),则可以使用数量为3的信号量。请注意,这并不能告诉您拥有哪些磁带驱动器,而只是告诉您一定数量。

同样,通过信号量,单个储物柜也可以锁定资源的多个实例,例如磁带到磁带的副本。如果您只有一种资源(例如您不想破坏的内存位置),则互斥锁更合适。

等效操作为:

Counting semaphore          Mutual exclusion semaphore
--------------------------  --------------------------
  Claim/decrease (P)                  Lock
  Release/increase (V)                Unlock

另外:如果您曾经想知道用于索取和释放信号量的奇怪信件,那是因为发明人是荷兰人。普通探针意味着尝试减少,而普通激素则意味着增加。


(a) ...或者可以将其视为完全不同于信号量的东西,考虑到它们几乎总是不同的用法,它可能会更安全。


好的,我也遇到了二进制信号量。什么时候应该使用二进制信号量,什么时候应该使用互斥锁?
Karthik Balaguru

从概念上讲,二进制信号量一个互斥量,它等效于一个计数的普通信号量。在概念的实现上可能存在差异,例如效率或资源所有权(可以由主张者以外的其他人释放,我不同意BTW -资源只能由主张它的线程释放) )。
paxdiablo

1
另一个潜在的实现差异是递归互斥体。因为只有一个资源,所以可能允许单个线程多次锁定它(只要它也多次释放它)。使用多实例资源并不是那么容易,因为您可能不知道线程是要再次声明另一个实例还是同一实例。
paxdiablo

1
他们解决了一个特定的问题。他们解决的问题是不太熟悉互斥的人,因此绝对不应轻视解决方案:-)
paxdiablo

5
互斥锁与二进制信号量完全不同。抱歉,这个定义是错误的
Peer Stritzinger 2010年

49

了解互斥锁不是计数为1的信号非常重要!

这就是为什么存在诸如二进制信号量之类的东西(实际上是计数为1的信号量)的原因。

互斥量和二进制信号量之间的区别是所有权原则:

互斥锁由任务获取,因此也必须由同一任务释放。这样就可以解决二进制信号量的一些问题(密码释放,递归死锁和优先级倒置)。

警告:我写了“使之成为可能”,这些问题是否得以解决以及如何解决取决于操作系统的实现。

由于互斥锁必须由同一任务释放,因此对于任务同步而言不是很好。但是,如果与条件变量结合使用,您将获得用于构建各种ipc原语的非常强大的构建块。

因此,我的建议是:如果您已实现干净的互斥锁和条件变量(例如POSIX pthreads),请使用它们。

仅在信号量完全适合您要解决的问题时才使用信号量,不要尝试构建其他原语(例如,将rw锁定在信号量之外,为此使用互斥量和条件变量)

有很多误解的互斥量和信号量。到目前为止,我发现的最好的解释是在这三部分文章中:

Mutex与信号量–第1部分:信号量

互斥与信号量–第2部分:互斥

Mutex与信号量–第3部分(最后一部分):互斥问题


该网站的网址包含时髦字符,因此无法正常工作...我正在努力
Peer Stritzinger 2010年

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_unlock(mutex_thing); // unlock mutex

信号量情况不同:

/* Task 1 - Producer */
sema_post(&sem);   // Send the signal

/* Task 2 - Consumer */
sema_wait(&sem);   // Wait for signal

有关更多说明,请参见http://www.netrino.com/node/202


2
你是对的。即使您使用的信号量只有一个,也暗示着您在做什么,而不是使用互斥量。
2010年

我不知道我同意这一点,虽然我不同意如此激烈,我将downvote你:-)你说,信号灯的使用模式是通知线程,但是这恰恰是互斥做的时候有另一个线程等待就此而言,信号量在没有线程时实际上没有sema_wait:-)在我看来,它们与资源有关,并且将通知传递给其他线程也是它的副作用(非常重要,从性能角度而言)保护。
paxdiablo

You say that the usage pattern of semaphores is to notify threads关于通知线程的一点。你可以调用sem_post从安全的一个信号处理器(pubs.opengroup.org/onlinepubs/009695399/functions/...),但它是不推荐给呼叫pthread_mutex_lockpthread_mutex_unlock从信号处理程序(manpages.ubuntu.com/manpages/lucid/man3/...

@paxdiablo:此互斥量二进制信号量之间的一个主要区别是保持引用计数。互斥锁或您可以说,任何条件互斥锁都不会维护与锁相关的任何计数,如sempahore用于保持计数的锁。因此sem_wait和sem_post保持计数。
Prak

9

请参阅“厕所示例”- -http : //pheatt.emporia.edu/courses/2010/cs557f10/hand07/Mutex%20vs_%20Semaphore.htm

互斥体:

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

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

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

信号:

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

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


7

尽量不要听起来很滑稽,但不能帮助自己。

您的问题应该是互斥量和信号量有什么区别?更确切地说,应该是“互斥量和信号量之间的关系是什么?”

(我会添加这个问题,但我百分百地确定一些狂热的主持人会在不了解差异和关系之间的差异的情况下将其作为重复项关闭。)

在对象术语中,我们可以观察到:

观察。1信号量包含互斥量

观察。2互斥量不是信号量,信号量也不是互斥量。

有一些信号量就像是互斥量一样,称为二进制信号量,但是它们确实不是NOT互斥量。

有一种特殊的成分称为Signaling(posix使用该名称的condition_variable),可以用互斥量制作信号量。将其视为通知源。如果两个或多个线程订阅了相同的通知源,则可以将它们的消息发送到ONE或ALL,以进行唤醒。

可能有一个或多个与信号量关联的计数器,这些计数器由互斥量保护。最简单的信号量方案是有一个计数器,该计数器可以为0或1。

就像季风雨一样,这就是混乱的地方。

计数器可以为0或1的信号量不是互斥体。

互斥体具有两种状态(0,1)和一种所有权(任务)。信号量具有互斥量,一些计数器和条件变量。

现在,发挥您的想象力,计数器的使用以及何时发出信号的每种组合都可以构成一种信号量。

  1. 具有值0或1的单个计数器,并在值变为1时发出信号,然后解锁等待信号的家伙之一==二进制信号量

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

  3. 单个计数器,其值从0到N,并在值到达N时发出信号,并在值小于N时锁定/等待(=屏障信号量)(如果他们不调用它,则应该这样做)。

现在到您的问题,什么时候使用什么。(或者说是正确的问题版本。3何时使用互斥锁以及何时使用二进制信号量,因为没有与非二进制信号量的比较。)在以下情况下请使用互斥锁:1.您想要自定义行为,而二进制不提供信号量,例如自旋锁或快速锁或递归锁。通常,您可以使用属性来自定义互斥锁,但是自定义信号灯只不过是编写新的信号灯而已。2.您想要轻量级的或更快的基元

当信号量恰好由您提供时,请使用信号量。

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

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


5

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

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

binary Semaphore =更多的是一种信号机制,任何其他更高优先级的线程如果愿意可以发信号并取得锁。


5

互斥是为了保护共享资源。
信号量是调度线程。

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


信号量:
假设我们需要进行如下计算:

c = a + b;

另外,我们需要一个功能 geta()要计算a的函数,一个getb()要计算b的函数和一个getc()要进行计算的函数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它才能发挥作用。t1t2

总之,信号量是使线程按逻辑顺序执行,而互斥量是为了保护共享资源。
因此,即使有人总是说互斥锁是具有初始值1的特殊信号量,它们也不是同一回事。您也可以这样说,但请注意,它们是在不同情况下使用的。即使可以做到,也不要一个人替换一个人。


卖票就是一个很好的例子。信号量示例尚不清楚(无论如何对我来说)。
祈祷的人

1
@prayagupd信号量示例是按某种顺序排列线程,而售票不需要任何顺序。如果有三个人:a,b和c。他们来买票时,我们根本不在乎买票的顺序。然而,如果我们做了这样一个计算:x = getx(); y = gety(); z = x + y;出于某种原因,我们用三个线程做三件事情,因为现在我们不能做线程的顺序是非常重要的x + y,除非getxgety已经完成。总之,当我们关心多线程的执行顺序时,将使用信号量。
伊夫

知道了 这听起来像障碍。我可以说,等到线程xy完成后,再计算z = x + y。我知道java有CyclicBarrier。另外,我不确定是否也可以说mapreduce信号量用例,因为reduce直到所有maps都完成,我才能知道。
祈祷

@prayagupd是的。你可以这样说。
伊夫(Yves)

2

以上所有答案的质量都很好,但这只是为了记住。互斥锁的名称来源于互斥,因此您有动力将互斥锁视为一次在两个之间互斥,如果我拥有它,只有在我释放它之后才能拥有它。另一方面,对于Semaphore来说,这种情况就不存在了,就像交通信号灯一样(Semaphore这个词也意味着)。


1

正如已经指出的那样,计数为一的信号量与“二进制”信号量是相同的,而“二进制”信号量则与互斥量相同。

我看到的信号量大于一个的数量是生产者/消费者情况,其中您有一个固定大小的队列。

然后,您有两个信号灯。最初将第一个信号量设置为队列中的项目数,第二个信号量设置为0。生产者对第一个信号量执行P操作,将其添加到队列中。并在第二个上执行V运算。使用者对第二个信号量执行P操作,从队列中删除,然后对第一个信号量执行V操作。

这样,无论何时生产者填充队列,生产者都会被阻止,而当队列为空时,消费者也会被阻止。


1

互斥锁是信号量的一种特殊情况。信号量允许几个线程进入临界区。创建信号量时,您可以定义关键部分中如何允许线程。当然,您的代码必须能够处理对该关键部分的多次访问。


-1

二进制信号量和互斥量是不同的。从OS角度来看,二进制信号量和计数信号量以相同的方式实现,并且二进制信号量的值可以为0或1。

Mutex- >只能用于一个关键部分的互斥的唯一目的。

信号量 ->可用于解决各种问题。二进制信号量可以用于信令,也可以解决互斥问题。当初始化为0时,它解决了信令问题;当初始化为1时,它解决了互斥问题。

当资源数量更多并且需要同步时,我们可以使用计数信号量。

在我的博客中,我详细讨论了这些主题。

https://designpatterns-oo-cplusplus.blogspot.com/2015/07/synchronization-primitives-mutex-and.html

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.