什么时候应该使用互斥锁,什么时候应该使用信号量
- 2024-10-17 08:47:00
- admin 原创
- 75
问题描述:
什么时候应该使用互斥锁,什么时候应该使用信号量?
解决方案 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
另外:如果您曾经对用于领取和释放信号量的奇怪字母(P
和V
)感到好奇,那是因为发明者是荷兰人。用荷兰语来说:
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 + b
unless 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) 和一个所有权 (任务)。信号量有一个互斥锁、一些计数器和一个条件变量。
现在,发挥您的想象力,计数器的使用和信号发送时间的每种组合都可以构成一种信号量。
单个计数器,值为 0 或 1,当值变为 1 时发出信号,然后解锁其中一个等待信号的人 == 二进制信号量
单个计数器,值从 0 到 N,当值小于 N 时发出信号,当值为 N == 计数信号量时锁定/等待
单个计数器,值从 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
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件