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

2024-10-17 08:47:00
admin
原创
74
摘要:问题描述:什么时候应该使用互斥锁,什么时候应该使用信号量?解决方案 1:以下是我记住何时使用什么的方法 -信号量:当您(线程)想要休眠直到其他线程告诉您唤醒时,请使用信号量。信号量“关闭”发生在一个线程(生产者)中,而信号量“启动”(对于同一信号量)发生在另一个线程(消费者)中,例如:在生产者-消费者问题中,...

问题描述:

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


解决方案 1:

以下是我记住何时使用什么的方法 -

信号量:
当您(线程)想要休眠直到其他线程告诉您唤醒时,请使用信号量。信号量“关闭”发生在一个线程(生产者)中,而信号量“启动”(对于同一信号量)发生在另一个线程(消费者)中,例如:在生产者-消费者问题中,生产者想要休眠直到至少一个缓冲区槽为空 - 只有消费者线程才能知道缓冲区槽何时为空。

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

自旋锁:
当您确实想使用互斥锁但线程不允许休眠时,请使用自旋锁。例如:操作系统内核中的中断处理程序绝不能休眠。如果休眠,系统将冻结/崩溃。如果您需要从中断处理程序向全局共享链接列表插入节点,请获取自旋锁 - 插入节点 - 释放自旋锁。

解决方案 2:

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

这就是存在二进制信号量(实际上是计数为 1 的信号量)的原因。

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

互斥锁由任务获取,因此也必须由同一任务释放。这样可以解决二进制信号量的几个问题(意外释放、递归死锁和优先级反转)。

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

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

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

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

互斥锁和信号量之间存在很多误解。到目前为止,我发现的最好的解释是这篇由 3 部分组成的文章:

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

互斥锁与信号量 – 第二部分:互斥锁

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

解决方案 3:

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

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

另一方面,信号量的数量是任意的,并且可以同时被多个锁定器锁定。并且它可能不需要由声明它的同一线程释放(但如果不是,您必须仔细跟踪当前负责它的线程,就像分配的内存一样)。

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

此外,使用信号量,单个锁定器可以锁定资源的多个实例,例如磁带到磁带的复制。如果您有一个资源(例如您不想破坏的内存位置),则互斥锁更合适。

等效操作为:

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

另外:如果您曾经对用于领取和释放信号量的奇怪字母(PV)感到好奇,那是因为发明者是荷兰人。用荷兰语来说:

  • Probeer te verlagen:意思是试图降低;

  • Verhogen:意思是增加。


(a) ... 或者它可以被认为是与信号量完全不同的东西,考虑到它们几乎总是不同的用途,这可能更安全。

解决方案 4:

虽然@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了解更多解释

解决方案 5:

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

互斥:

是厕所的钥匙。一个人可以同时拥有钥匙 - 占用厕所。完成后,该人将钥匙交给(释放)队列中的下一个人。

官方说法:“互斥锁通常用于序列化对一段可重入代码的访问,该代码不能由多个线程同时执行。互斥锁对象只允许一个线程进入受控部分,迫使其他试图访问该部分的线程等待,直到第一个线程退出该部分。”参考:Symbian 开发者库

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

信号:

是可用的相同厕所钥匙的数量。例如,假设我们有四个厕所,锁和钥匙都相同。信号量计数(钥匙数量)在开始时设置为 4(所有四个厕所都是免费的),然后随着人们的进入,计数值会减少。如果所有厕所都满了,即没有剩余的空闲钥匙,信号量计数为 0。现在,当一个人离开厕所时,信号量会增加到 1(一个空闲钥匙),并交给队列中的下一个人。

正式定义:“信号量限制了共享资源同时使用的用户数量,最高可达最大值。线程可以请求访问资源(减少信号量),也可以发出信号表示他们已结束使用资源(增加信号量)。”参考:Symbian 开发者库

解决方案 6:

互斥锁用于保护共享资源,

信号量用于调度线程。

互斥锁:

假设有几张票要出售,我们可以模拟多人同时买票的情况,每人都是一个买票线程,显然我们需要使用互斥锁来保护票,因为票是共享资源。

信号量:

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

c = a + b;

此外,我们需要一个函数geta()来计算a,一个函数getb()来计算b和一个函数getc()来执行计算c = a + b

显然我们不能做c = a + bunless geta()and getb()have been finishes,

如果三个函数是三个线程的话,我们需要调度这三个线程。

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`t2`

总之,信号量是让线程按逻辑顺序执行,而互斥量是保护共享资源。

所以它们不是一回事,尽管有些人总是说互斥量是一种初始值为 1 的特殊信号量。你也可以这样说,但请注意它们的用法不同。即使你可以这样做,也不要用一个替换另一个。

解决方案 7:

我试图不让自己听起来显得滑稽,但我控制不住自己。

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

(我本来想添加这个问题,但我百分之百确定一些过分热心的版主会将其关闭为重复,而不理解差异和关系之间的区别。)

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

观察.1 信号量包含互斥锁

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

有一些信号量表现得就像互斥量,称为二进制信号量,但它们根本不是互斥量。

有一种称为 Signalling 的特殊成分(posix 使用 condition_variable 作为该名称),需要用互斥体来创建信号量。将其视为通知源。如果两个或多个线程订阅了同一个通知源,则可以向其中一个或所有线程发送消息以唤醒它们。

信号量可以有一个或多个计数器,这些计数器由互斥锁保护。信号量最简单的情况是,有一个计数器,可以是 0 或 1。

混乱就像季风雨一样袭来。

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

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

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

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

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

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

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

当信号量能够提供您需要的东西时,就使用信号量。

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

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

解决方案 8:

我觉得问题应该是互斥锁和二进制信号量的区别。

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

二进制信号量 = 它更像是一种信号机制,任何其他更高优先级的线程如果需要都可以发出信号并获取锁。

解决方案 9:

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

解决方案 10:

正如指出的那样,计数为 1 的信号量与“二进制”信号量相同,而后者又与互斥锁相同。

我所见过的计数大于一的信号量主要用于生产者/消费者的情况,在这种情况下你有一个固定大小的队列。

那么你就有两个信号量了。第一个信号量最初设置为队列中的项目数,第二个信号量设置为 0。生产者对第一个信号量执行 P 操作,将其添加到队列中,并对第二个信号量执行 V 操作。消费者对第二个信号量执行 P 操作,将其从队列中移除,然后对第一个信号量执行 V 操作。

这样,只要生产者填满队列,就会被阻塞,只要队列为空,就会被阻塞。

解决方案 11:

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

解决方案 12:

我发现@Peer Stritzinger 的答案是正确的。

我想在他的回答中添加以下Programming with POSIX Threads来自 的书中的引文David R Butenhof。在第 3 章第 52 页,作者写道(重点是我加的):

当调用线程已经锁定了互斥锁时,您无法锁定该互斥锁。尝试这样做的结果可能是返回错误 (EDEADLK),也可能是自死锁,不幸的线程将永远等待。您无法解锁已解锁的互斥锁或由另一个线程锁定的互斥锁。锁定的互斥锁由锁定它们的线程拥有。如果您需要“无主”锁,请使用信号量。第 6.6.6 节讨论了信号量)

考虑到这一点,下面的一段代码说明了使用大小为 1 的信号量替代互斥锁的危险。

sem = Semaphore(1)
counter = 0 // shared variable
----

Thread 1

for (i in 1..100):
  sem.lock()
  ++counter
  sem.unlock()
----

Thread 2

for (i in 1..100):
  sem.lock()
  ++counter
  sem.unlock()
----

Thread 3

sem.unlock()
thread.sleep(1.sec)
sem.lock()

如果仅针对线程 1 和 2,计数器的最终值应为 200。但是,如果错误地将该信号量引用泄露给另一个线程并调用unlock,则不会获得互斥。使用互斥锁,根据定义,这种行为是不可能的。

解决方案 13:

二进制信号量和互斥量不同。从操作系统的角度来看,二进制信号量和计数信号量的实现方式相同,二进制信号量的值为 0 或 1。

互斥锁-> 只能用于代码关键部分的互斥目的。

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

当资源数量较多,需要同步时,可以使用计数信号量。

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

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

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用