什么时候应该使用互斥锁,什么时候应该使用信号量?
什么时候应该使用互斥锁,什么时候应该使用信号量?
Answers:
这是我记得何时使用什么的方式-
信号: 当您(线程)想要睡觉时使用信号量,直到其他线程告诉您醒来。信号量“下降”发生在一个线程(生产者)中,信号量“上升”(对于相同的信号量)发生在另一个线程(消费者)中,例如:在生产者-消费者问题中,生产者想要休眠直到至少一个缓冲区为空-仅使用者线程可以知道缓冲区插槽何时为空。
互斥锁: 当您(线程)想要执行不应同时由任何其他线程执行的代码时,请使用互斥锁。互斥锁“向下”发生在一个线程中,互斥锁“向上” 必须稍后在同一线程中发生。例如:如果要从全局链表中删除一个节点,则在删除该节点时,您不希望其他线程随指针乱码。当您获取一个互斥锁并忙于删除节点时,如果另一个线程试图获取相同的互斥锁,则它将进入休眠状态,直到您释放该互斥锁。
自旋锁: 使用一个自旋锁,当你真的想用一个互斥体,但你的线程不允许睡觉。例如:OS内核中的中断处理程序永远不能休眠。如果这样做,系统将冻结/崩溃。如果需要从中断处理程序中将节点插入到全局共享的链表中,请获取自旋锁-插入节点-释放自旋锁。
互斥锁是互斥对象,类似于信号量,但一次只能允许一个储物柜,并且其所有权限制可能比信号量更严格。
可以认为它等同于普通计数信号量(计数为1),并且要求它只能由锁定它的同一线程释放(a)。
另一方面,信号量具有任意数量,并且可以同时被那么多储物柜锁定。而且它可能并不需要由声明它的线程来释放它(但是,如果没有,则必须仔细地跟踪谁当前对此负责,就像分配的内存一样)。
因此,如果您有多个资源实例(例如三个磁带驱动器),则可以使用数量为3的信号量。请注意,这并不能告诉您拥有哪些磁带驱动器,而只是告诉您一定数量。
同样,通过信号量,单个储物柜也可以锁定资源的多个实例,例如磁带到磁带的副本。如果您只有一种资源(例如您不想破坏的内存位置),则互斥锁更合适。
等效操作为:
Counting semaphore Mutual exclusion semaphore
-------------------------- --------------------------
Claim/decrease (P) Lock
Release/increase (V) Unlock
另外:如果您曾经想知道用于索取和释放信号量的奇怪信件,那是因为发明人是荷兰人。普通探针意味着尝试减少,而普通激素则意味着增加。
(a) ...或者可以将其视为完全不同于信号量的东西,考虑到它们几乎总是不同的用法,它可能会更安全。
了解互斥锁不是计数为1的信号非常重要!
这就是为什么存在诸如二进制信号量之类的东西(实际上是计数为1的信号量)的原因。
互斥量和二进制信号量之间的区别是所有权原则:
互斥锁由任务获取,因此也必须由同一任务释放。这样就可以解决二进制信号量的一些问题(密码释放,递归死锁和优先级倒置)。
警告:我写了“使之成为可能”,这些问题是否得以解决以及如何解决取决于操作系统的实现。
由于互斥锁必须由同一任务释放,因此对于任务同步而言不是很好。但是,如果与条件变量结合使用,您将获得用于构建各种ipc原语的非常强大的构建块。
因此,我的建议是:如果您已实现干净的互斥锁和条件变量(例如POSIX pthreads),请使用它们。
仅在信号量完全适合您要解决的问题时才使用信号量,不要尝试构建其他原语(例如,将rw锁定在信号量之外,为此使用互斥量和条件变量)
有很多误解的互斥量和信号量。到目前为止,我发现的最好的解释是在这三部分文章中:
虽然@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
sema_wait
:-)在我看来,它们既与资源有关,并且将通知传递给其他线程也是它的副作用(非常重要,从性能角度而言)保护。
You say that the usage pattern of semaphores is to notify threads
关于通知线程的一点。你可以调用sem_post
从安全的一个信号处理器(pubs.opengroup.org/onlinepubs/009695399/functions/...),但它是不推荐给呼叫pthread_mutex_lock
和pthread_mutex_unlock
从信号处理程序(manpages.ubuntu.com/manpages/lucid/man3/...)
请参阅“厕所示例”- -http : //pheatt.emporia.edu/courses/2010/cs557f10/hand07/Mutex%20vs_%20Semaphore.htm:
互斥体:
是上厕所的钥匙。一个人可以当时拥有钥匙-上厕所。完成后,此人将密钥释放(释放)给队列中的下一个人。
正式地:“ Mutexe通常用于序列化不能由多个线程同时执行的可重入代码段的访问。互斥对象仅允许一个线程进入受控段,从而迫使其他试图获取访问权限的线程该部分等待,直到第一个线程从该部分退出。” 参考:Symbian开发人员库
(互斥量实际上是值为1的信号量。)
信号:
是免费的相同马桶钥匙的数量。例如,假设我们有四个具有相同锁和钥匙的卫生间。信号量计数(键的计数)在开始时设置为4(所有四个洗手间都是免费的),然后随着人们的进来,计数值递减。没有剩余的可用键,信号量为0。一个人离开洗手间,信号量增加到1(一个自由键),并交给队列中的下一个人。
正式地:“信号量将共享资源的并发用户数限制为最大数量。线程可以请求访问该资源(减少信号量),并且可以发出信号,表明他们已经使用完资源(增加信号量)。 ” 参考:Symbian开发人员库
尽量不要听起来很滑稽,但不能帮助自己。
您的问题应该是互斥量和信号量有什么区别?更确切地说,应该是“互斥量和信号量之间的关系是什么?”
(我会添加这个问题,但我百分百地确定一些狂热的主持人会在不了解差异和关系之间的差异的情况下将其作为重复项关闭。)
在对象术语中,我们可以观察到:
观察。1信号量包含互斥量
观察。2互斥量不是信号量,信号量也不是互斥量。
有一些信号量就像是互斥量一样,称为二进制信号量,但是它们确实不是NOT互斥量。
有一种特殊的成分称为Signaling(posix使用该名称的condition_variable),可以用互斥量制作信号量。将其视为通知源。如果两个或多个线程订阅了相同的通知源,则可以将它们的消息发送到ONE或ALL,以进行唤醒。
可能有一个或多个与信号量关联的计数器,这些计数器由互斥量保护。最简单的信号量方案是有一个计数器,该计数器可以为0或1。
就像季风雨一样,这就是混乱的地方。
计数器可以为0或1的信号量不是互斥体。
互斥体具有两种状态(0,1)和一种所有权(任务)。信号量具有互斥量,一些计数器和条件变量。
现在,发挥您的想象力,计数器的使用以及何时发出信号的每种组合都可以构成一种信号量。
具有值0或1的单个计数器,并在值变为1时发出信号,然后解锁等待信号的家伙之一==二进制信号量
单个计数器,其值从0到N,并在值小于N时发出信号,并在值N时锁定/等待= =计数信号量
单个计数器,其值从0到N,并在值到达N时发出信号,并在值小于N时锁定/等待(=屏障信号量)(如果他们不调用它,则应该这样做)。
现在到您的问题,什么时候使用什么。(或者说是正确的问题版本。3何时使用互斥锁以及何时使用二进制信号量,因为没有与非二进制信号量的比较。)在以下情况下请使用互斥锁:1.您想要自定义行为,而二进制不提供信号量,例如自旋锁或快速锁或递归锁。通常,您可以使用属性来自定义互斥锁,但是自定义信号灯只不过是编写新的信号灯而已。2.您想要轻量级的或更快的基元
当信号量恰好由您提供时,请使用信号量。
如果您不了解二进制信号量的实现所提供的内容,那么恕我直言,请使用互斥体。
最后阅读一本书,而不是仅仅依靠SO。
互斥是为了保护共享资源。
信号量是调度线程。
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
它才能发挥作用。t1
t2
总之,信号量是使线程按逻辑顺序执行,而互斥量是为了保护共享资源。
因此,即使有人总是说互斥锁是具有初始值1的特殊信号量,它们也不是同一回事。您也可以这样说,但请注意,它们是在不同情况下使用的。即使可以做到,也不要一个人替换一个人。
x = getx(); y = gety(); z = x + y;
出于某种原因,我们用三个线程做三件事情,因为现在我们不能做线程的顺序是非常重要的x + y
,除非getx
和gety
已经完成。总之,当我们关心多线程的执行顺序时,将使用信号量。
x
和y
完成后,再计算z = x + y
。我知道java有CyclicBarrier
。另外,我不确定是否也可以说mapreduce
信号量用例,因为reduce
直到所有map
s都完成,我才能知道。
以上所有答案的质量都很好,但这只是为了记住。互斥锁的名称来源于互斥,因此您有动力将互斥锁视为一次在两个之间互斥,如果我拥有它,只有在我释放它之后才能拥有它。另一方面,对于Semaphore来说,这种情况就不存在了,就像交通信号灯一样(Semaphore这个词也意味着)。
互斥锁是信号量的一种特殊情况。信号量允许几个线程进入临界区。创建信号量时,您可以定义关键部分中如何允许线程。当然,您的代码必须能够处理对该关键部分的多次访问。
二进制信号量和互斥量是不同的。从OS角度来看,二进制信号量和计数信号量以相同的方式实现,并且二进制信号量的值可以为0或1。
Mutex- >只能用于一个关键部分的互斥的唯一目的。
信号量 ->可用于解决各种问题。二进制信号量可以用于信令,也可以解决互斥问题。当初始化为0时,它解决了信令问题;当初始化为1时,它解决了互斥问题。
当资源数量更多并且需要同步时,我们可以使用计数信号量。
在我的博客中,我详细讨论了这些主题。
https://designpatterns-oo-cplusplus.blogspot.com/2015/07/synchronization-primitives-mutex-and.html