信号量是一种编程概念,经常用于解决多线程问题。 我对社区的问题:
什么是信号量,如何使用?
#1楼
因此,想象每个人都在尝试去洗手间,而洗手间只有一定数量的钥匙。 现在,如果没有足够的键,该人需要等待。 因此,可以将信号量视为代表可用于浴室(系统资源)的那些键集,以供不同进程(浴室行进者)请求访问。
现在想象一下试图同时去洗手间的两个过程。 那不是一个好情况,并且使用信号量来防止这种情况。 不幸的是,信号量是一种自愿机制,过程(我们的洗手间)可以忽略它(即,即使有钥匙,仍然有人可以将门打开)。
二进制/互斥量和计数信号量之间也存在差异。
在http://www.cs.columbia.edu/~jae/4118/lect/L05-ipc.html上查看讲义。
#2楼
构建并发程序有两个基本概念-同步和互斥。 我们将看到这两种类型的锁(信号灯通常是一种锁定机制)如何帮助我们实现同步和互斥。
信号量是一种编程结构,可通过实现同步和互斥来帮助我们实现并发。 信号量有两种类型,二进制和计数。
信号量包括两个部分:一个计数器和一个等待访问特定资源的任务列表。 信号量执行两项操作:wait(P)[就像获取锁一样],以及release(V)[类似于释放锁] –这是一个可以对信号量执行的仅有的两项操作。 在二进制信号量中,计数器在逻辑上介于0和1之间。您可以认为它类似于具有两个值的锁:打开/关闭。 计数信号量具有多个计数值。
重要的是要理解,信号量计数器跟踪不必阻塞的任务数量,即它们可以取得进展。 任务被阻止,并且仅在计数器为零时才将其自己添加到信号量列表中。 因此,如果任务无法进行,则将其添加到P()例程的列表中,然后使用V()例程“释放”任务。
现在,很明显地看到如何使用二进制信号量来解决同步和互斥-它们本质上是锁。
例如 同步:
thread A{
semaphore &s; //locks/semaphores are passed by reference! think about why this is so.
A(semaphore &s): s(s){} //constructor
foo(){
...
s.P();
;// some block of code B2
...
}
//thread B{
semaphore &s;
B(semaphore &s): s(s){} //constructor
foo(){
...
...
// some block of code B1
s.V();
..
}
main(){
semaphore s(0); // we start the semaphore at 0 (closed)
A a(s);
B b(s);
}
在上面的示例中,B2仅在B1完成执行后才能执行。 假设线程A首先执行-进入sem.P(),然后等待,因为计数器为0(关闭)。 线程B出现,完成B1,然后释放线程A-然后完成B2。 因此,我们实现了同步。
现在,让我们看一下带有二进制信号量的互斥:
thread mutual_ex{
semaphore &s;
mutual_ex(semaphore &s): s(s){} //constructor
foo(){
...
s.P();
//critical section
s.V();
...
...
s.P();
//critical section
s.V();
...
}
main(){
semaphore s(1);
mutual_ex m1(s);
mutual_ex m2(s);
}
互斥也非常简单-m1和m2无法同时进入关键部分。 因此,每个线程都使用相同的信号量为其两个关键部分提供互斥。 现在,可以有更大的并发性吗? 取决于关键部分。 (想一想还有其他方法可以使用信号量实现互斥..提示提示:我是否只需要使用一个信号量?)
信号量计数:具有多个值的信号量。 让我们看看这意味着什么-一个具有多个值的锁? 因此,打开,关闭和……嗯。 互斥或同步中的多级锁有什么用?
让我们更简单地选择两个:
使用计数信号量进行同步:假设您有3个任务-#1和2在3之后要执行。您将如何设计同步?
thread t1{
...
s.P();
//block of code B1
thread t2{
...
s.P();
//block of code B2
thread t3{
...
//block of code B3
s.V();
s.V();
}
因此,如果您的信号量从关闭开始,请确保将t1和t2阻止添加到了信号量列表中。 然后所有重要的t3出现,完成其业务并释放t1和t2。 他们以什么顺序被释放? 取决于信号量列表的实现。 可以是FIFO,可以基于某些特定的优先级等。 (注意:如果希望以特定顺序执行t1和t2,并且不知道信号量的实现,请考虑如何布置P和V;)
(发现:如果V的数量大于P的数量会发生什么?)
互斥使用计数信号量:我希望您为此构建自己的伪代码(使您更好地理解!)-但基本概念是:counter = N的计数信号量使N个任务可以自由进入关键部分。 这意味着您有N个任务(或线程,如果您愿意)进入关键部分,但是第N + 1个任务被阻止(进入我们最喜欢的阻止任务列表),并且只有当有人V成为信号量时才通过至少一次。 因此,信号量计数器现在不再在0和1之间摆动,而是在0和N之间摆动,从而允许N个任务自由进出,阻止任何人!
现在,天哪,你为什么需要这么愚蠢的东西? 互斥的全部目的不是让一个以上的人访问资源吗? (提示提示...您的计算机并不总是只有一个驱动器,对吗??)
考虑一下 :是否可以通过单独使用计数信号量来实现互斥? 如果您有10个资源实例,并且有10个线程(通过计数信号量)进入并尝试使用第一个实例怎么办?
#3楼
信号量是一种锁定资源的方法,可以确保在执行一段代码时,只有该段代码可以访问该资源。 这样可以防止两个线程同时访问资源,这可能会导致问题。
#4楼
@克雷格:
信号量是一种锁定资源的方法,可以确保在执行一段代码时,只有该段代码可以访问该资源。 这样可以防止两个线程同时访问资源,这可能会导致问题。
这不仅限于一个线程。 可以将信号量配置为允许固定数量的线程访问资源。
#5楼
迈克尔·巴尔(Michael Barr) 揭秘的互斥量和信号量文章是一个简短的简短介绍,介绍了互斥量和信号量与众不同的原因以及何时以及不应该使用它们。 我在这里摘录了几个关键段落。
关键是互斥锁应用于保护共享资源,而信号灯应用于信令。 通常,您不应使用信号量来保护共享资源,也不应使用互斥体来发信号。 例如,在使用信号量来保护共享资源方面,保镖类比存在一些问题-您可以通过这种方式使用它们,但可能会导致难以诊断错误。
尽管互斥量和信号量在实现上有一些相似之处,但应始终以不同的方式使用它们。
最常见(但仍然不正确)的答案是,互斥量和信号量非常相似,唯一的区别是信号量可以大于一个。 几乎所有工程师似乎都正确地理解了互斥锁是一种二进制标志,用于通过确保在代码的关键部分内相互排斥来保护共享资源。 但是,当被问及如何使用“计数信号量”时,大多数工程师(仅凭信心而定)表达了一些教科书的观点,即它们被用来保护多个等效资源。
...
在这一点上,使用浴室钥匙作为保护共享资源(浴室)的想法做出了一个有趣的类比。 如果商店只有一个浴室,那么一个钥匙就足以保护该资源并防止多个人同时使用它。
如果有多个浴室,则可能会想像一个浴室那样对它们进行键控并做出多个键-这类似于信号灯被滥用。 拥有钥匙后,您实际上并不知道哪个浴室可用,如果您沿着这条路走,您可能最终将使用互斥锁来提供该信息,并确保您不上已被占用的浴室。
信号量是保护几个基本相同的资源的错误工具,但这是许多人想到并使用它的人。 保镖的类比明显不同-没有几种相同类型的资源,而是有一种资源可以接受多个同时用户。 我想可以在这种情况下使用信号量,但是在现实世界中很少有此类比喻真正成立的情况-经常有几种相同类型,但仍然有个别资源(例如浴室)无法使用这条路。
...
信号量的正确用法是用于从一个任务向另一个任务发信号。 互斥锁应始终由使用它保护的共享资源的每个任务按此顺序获取和释放。 相比之下,使用信号量的任务要么发出信号,要么等待,而不是同时发出信号或等待。 例如,任务1可能包含用于在按下“电源”按钮时张贴(即发出信号或递增信号)特定信号量的代码,而唤醒显示器的任务2则悬停在同一信号量上。 在这种情况下,一个任务是事件信号的产生者; 另一个消费者。
...
这里要指出的一点是,互斥锁会以一种不好的方式干扰实时操作系统,从而导致优先级倒置,由于资源共享,优先级较低的任务可能会在优先级较高的任务之前执行。 简而言之,当优先级较低的任务使用互斥锁抢占资源A,然后尝试抢夺B,但由于B不可用而暂停时,就会发生这种情况。 在等待期间,出现了一个更高优先级的任务,需要A,但是它已经被捆绑,并且由于等待B而无法运行。有很多方法可以解决此问题,但是通常它是固定的通过更改互斥锁和任务管理器。 在这种情况下,互斥锁比二进制信号量要复杂得多,并且在这种情况下使用信号量会导致优先级倒置,因为任务管理器没有意识到优先级倒置并且无法采取措施对其进行纠正。
...
现代互斥量和信号量之间广泛混淆的原因是历史性的,因为它可以追溯到1974年Djikstra发明的信号量(本文中为大写“ S”)。 在此之前,计算机科学家所知的任何中断安全任务同步和信令机制都无法有效地扩展以用于两个以上的任务。 Dijkstra的革命性,安全且可扩展的信号量已应用于关键部分保护和信号发送。 于是混乱就开始了。
但是,后来出现了基于优先级的抢占式RTOS(例如,VRTX,约1980年),建立RMA的学术论文以及由优先级反转引起的问题以及关于优先级的论文之后,对操作系统开发人员而言这变得显而易见。在1990年的3继承协议中,很明显,互斥锁不仅是带有二进制计数器的信号量。
互斥体:资源共享
信号量:信号
在未仔细考虑副作用的情况下,请勿将另一种药物用于另一种药物。
来源:oschina
链接:https://my.oschina.net/u/3797416/blog/3191950