multithreading 什么是信号量?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34519/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is a semaphore?
提问by bmurphy1976
A semaphore is a programming concept that is frequently used to solve multi-threading problems. My question to the community:
信号量是一种经常用于解决多线程问题的编程概念。我对社区的问题:
What is a semaphore and how do you use it?
什么是信号量以及如何使用它?
回答by Patrik Svensson
Think of semaphores as bouncers at a nightclub. There are a dedicated number of people that are allowed in the club at once. If the club is full no one is allowed to enter, but as soon as one person leaves another person might enter.
将信号量视为夜总会的保镖。有一定数量的人可以同时进入俱乐部。如果俱乐部已满,则不允许任何人进入,但一旦一个人离开,另一个人就可以进入。
It's simply a way to limit the number of consumers for a specific resource. For example, to limit the number of simultaneous calls to a database in an application.
这只是一种限制特定资源的消费者数量的方法。例如,限制应用程序中同时调用数据库的次数。
Here is a very pedagogic example in C# :-)
这是一个非常具有教育意义的 C# 示例:-)
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace TheNightclub
{
public class Program
{
public static Semaphore Bouncer { get; set; }
public static void Main(string[] args)
{
// Create the semaphore with 3 slots, where 3 are available.
Bouncer = new Semaphore(3, 3);
// Open the nightclub.
OpenNightclub();
}
public static void OpenNightclub()
{
for (int i = 1; i <= 50; i++)
{
// Let each guest enter on an own thread.
Thread thread = new Thread(new ParameterizedThreadStart(Guest));
thread.Start(i);
}
}
public static void Guest(object args)
{
// Wait to enter the nightclub (a semaphore to be released).
Console.WriteLine("Guest {0} is waiting to entering nightclub.", args);
Bouncer.WaitOne();
// Do some dancing.
Console.WriteLine("Guest {0} is doing some dancing.", args);
Thread.Sleep(500);
// Let one guest out (release one semaphore).
Console.WriteLine("Guest {0} is leaving the nightclub.", args);
Bouncer.Release(1);
}
}
}
回答by Adam Davis
The article Mutexes and Semaphores Demystifiedby Michael Barr is a great short introduction into what makes mutexes and semaphores different, and when they should and should not be used. I've excerpted several key paragraphs here.
Michael Barr撰写的Mutexes and Semaphores Demystified一文对互斥量和信号量的不同之处进行了简短的介绍,以及何时应该使用和不应该使用它们。我在这里摘录了几个关键段落。
The key point is that mutexes should be used to protect shared resources, while semaphores should be used for signaling. You should generally not use semaphores to protect shared resources, nor mutexes for signaling. There are issues, for instance, with the bouncer analogy in terms of using semaphores to protect shared resources - you can use them that way, but it may cause hard to diagnose bugs.
关键是互斥量应该用于保护共享资源,而信号量应该用于信令。您通常不应该使用信号量来保护共享资源,也不应该使用互斥体来发送信号。例如,就使用信号量来保护共享资源而言,bouncer 类比存在一些问题——您可以以这种方式使用它们,但可能会导致难以诊断错误。
While mutexes and semaphores have some similarities in their implementation, they should always be used differently.
The most common (but nonetheless incorrect) answer to the question posed at the top is that mutexes and semaphores are very similar, with the only significant difference being that semaphores can count higher than one. Nearly all engineers seem to properly understand that a mutex is a binary flag used to protect a shared resource by ensuring mutual exclusion inside critical sections of code. But when asked to expand on how to use a "counting semaphore," most engineers—varying only in their degree of confidence—express some flavor of the textbook opinion that these are used to protect several equivalent resources.
虽然互斥体和信号量在实现上有一些相似之处,但它们的使用方式应该始终不同。
对顶部提出的问题最常见(但仍然不正确)的答案是互斥体和信号量非常相似,唯一显着的区别是信号量可以计数大于 1。几乎所有工程师似乎都正确理解互斥锁是一个二进制标志,用于通过确保代码关键部分内的互斥来保护共享资源。但是,当被要求扩展如何使用“计数信号量”时,大多数工程师(仅在他们的信心程度方面有所不同)表达了一些教科书观点,即这些用于保护几个等效资源。
...
...
At this point an interesting analogy is made using the idea of bathroom keys as protecting shared resources - the bathroom. If a shop has a single bathroom, then a single key will be sufficient to protect that resource and prevent multiple people from using it simultaneously.
在这一点上,使用浴室钥匙作为保护共享资源 - 浴室的想法进行了一个有趣的类比。如果一家商店只有一个浴室,那么一把钥匙就足以保护该资源并防止多人同时使用它。
If there are multiple bathrooms, one might be tempted to key them alike and make multiple keys - this is similar to a semaphore being mis-used. Once you have a key you don't actually know which bathroom is available, and if you go down this path you're probably going to end up using mutexes to provide that information and make sure you don't take a bathroom that's already occupied.
如果有多个浴室,人们可能会试图将它们相同地键并制作多个键 - 这类似于误用信号量。一旦你有了一把钥匙,你实际上并不知道哪个浴室可用,如果你沿着这条路走下去,你可能最终会使用互斥锁来提供这些信息,并确保你没有使用已经被占用的浴室.
A semaphore is the wrong tool to protect several of the essentially same resource, but this is how many people think of it and use it. The bouncer analogy is distinctly different - there aren't several of the same type of resource, instead there is one resource which can accept multiple simultaneous users. I suppose a semaphore can be used in such situations, but rarely are there real-world situations where the analogy actually holds - it's more often that there are several of the same type, but still individual resources, like the bathrooms, which cannot be used this way.
信号量是保护几个本质上相同的资源的错误工具,但这是多少人想到并使用它的工具。保镖类比明显不同 - 没有几种相同类型的资源,而是有一种资源可以同时接受多个用户。我想在这种情况下可以使用信号量,但在现实世界中很少有类似的实际情况 - 更常见的是有几个相同类型的资源,但仍然是个人资源,如浴室,不能使用这边走。
...
...
The correct use of a semaphore is for signaling from one task to another. A mutex is meant to be taken and released, always in that order, by each task that uses the shared resource it protects. By contrast, tasks that use semaphores either signal or wait—not both. For example, Task 1 may contain code to post (i.e., signal or increment) a particular semaphore when the "power" button is pressed and Task 2, which wakes the display, pends on that same semaphore. In this scenario, one task is the producer of the event signal; the other the consumer.
信号量的正确使用是用于从一项任务到另一项任务的信号。每个使用它保护的共享资源的任务总是按照这个顺序获取和释放互斥锁。相比之下,使用信号量的任务要么发出信号要么等待——而不是两者兼而有之。例如,当按下“电源”按钮时,任务 1 可能包含发送(即,发出信号或增加)特定信号量的代码,而唤醒显示器的任务 2 挂起在同一信号量上。在这种情况下,一项任务是事件信号的生产者;另一个是消费者。
...
...
Here an important point is made that mutexes interfere with real time operating systems in a bad way, causing priority inversion where a less important task may be executed before a more important task because of resource sharing. In short, this happens when a lower priority task uses a mutex to grab a resource, A, then tries to grab B, but is paused because B is unavailable. While it's waiting, a higher priority task comes along and needs A, but it's already tied up, and by a process that isn't even running because it's waiting for B. There are many ways to resolve this, but it most often is fixed by altering the mutex and task manager. The mutex is much more complex in these cases than a binary semaphore, and using a semaphore in such an instance will cause priority inversions because the task manager is unaware of the priority inversion and cannot act to correct it.
这里有一个重要的观点,即互斥体以一种糟糕的方式干扰实时操作系统,导致优先级反转,因为资源共享,一个不太重要的任务可能会在更重要的任务之前执行。简而言之,当较低优先级的任务使用互斥锁来抓取资源 A,然后尝试抓取 B,但由于 B 不可用而暂停时,就会发生这种情况。当它在等待时,一个更高优先级的任务出现并需要 A,但它已经被一个进程捆绑了,因为它在等待 B,它甚至没有运行。有很多方法可以解决这个问题,但通常是固定的通过更改互斥锁和任务管理器。在这些情况下,互斥锁比二进制信号量要复杂得多,
...
...
The cause of the widespread modern confusion between mutexes and semaphores is historical, as it dates all the way back to the 1974 invention of the Semaphore (capital "S", in this article) by Djikstra. Prior to that date, none of the interrupt-safe task synchronization and signaling mechanisms known to computer scientists was efficiently scalable for use by more than two tasks. Dijkstra's revolutionary, safe-and-scalable Semaphore was applied in both critical section protection and signaling. And thus the confusion began.
However, it later became obvious to operating system developers, after the appearance of the priority-based preemptive RTOS (e.g., VRTX, ca. 1980), publication of academic papers establishing RMA and the problems caused by priority inversion, and a paper on priority inheritance protocols in 1990, 3 it became apparent that mutexes must be more than just semaphores with a binary counter.
现代普遍混淆互斥体和信号量的原因是历史性的,因为它可以追溯到 1974 年由 Djikstra 发明的信号量(本文中的大写字母“S”)。在此之前,计算机科学家已知的中断安全任务同步和信号机制都不能有效地扩展以供两个以上的任务使用。Dijkstra 的革命性、安全和可扩展的信号量应用于临界区保护和信号传输。于是混乱开始了。
然而,在基于优先级的抢占式 RTOS(例如 VRTX,约 1980 年)出现、建立 RMA 的学术论文和优先级倒置引起的问题以及优先级倒置的论文发表后,操作系统开发人员逐渐明白了这一点。在 1990 年的继承协议中,3 很明显,互斥体必须不仅仅是带有二进制计数器的信号量。
Mutex: resource sharing
互斥:资源共享
Semaphore: signaling
信号量:信令
Don't use one for the other without careful consideration of the side effects.
不要在没有仔细考虑副作用的情况下将一种用于另一种。
回答by Michael Haren
Mutex: exclusive-member access to a resource
互斥:对资源的独占访问
Semaphore: n-member access to a resource
信号量:对资源的 n 成员访问
That is, a mutex can be used to syncronize access to a counter, file, database, etc.
也就是说,互斥锁可用于同步对计数器、文件、数据库等的访问。
A sempahore can do the same thing but supports a fixed number of simultaneous callers. For example, I can wrap my database calls in a semaphore(3) so that my multithreaded app will hit the database with at most 3 simultaneous connections. All attempts will block until one of the three slots opens up. They make things like doing naive throttling really, really easy.
信号量可以做同样的事情,但支持固定数量的同时调用者。例如,我可以将我的数据库调用包装在一个 semaphore(3) 中,这样我的多线程应用程序最多可以同时使用 3 个连接访问数据库。所有尝试都将阻塞,直到三个插槽之一打开。它们使诸如进行天真的节流之类的事情变得非常非常容易。
回答by NKumar
Consider, a taxi that can accommodate a total of 3(rear)+2(front) persons including the driver. So, a semaphore
allows only 5 persons inside a car at a time.
And a mutex
allows only 1 person on a single seat of the car.
考虑一下,一辆出租车可以容纳总共 3(后)+2(前)人,包括司机。因此,a 一次semaphore
只允许 5 人在车内。而 amutex
只允许 1 人坐在汽车的一个座位上。
Therefore, Mutex
is to allow exclusive access for a resource (like an OS thread) while a Semaphore
is to allow access for nnumber of resources at a time.
因此,Mutex
是允许对资源(如操作系统线程)进行独占访问,而 aSemaphore
是允许一次访问n个资源。
回答by Mats Fredriksson
@Craig:
@克雷格:
A semaphore is a way to lock a resource so that it is guaranteed that while a piece of code is executed, only this piece of code has access to that resource. This keeps two threads from concurrently accesing a resource, which can cause problems.
信号量是一种锁定资源的方法,以保证在执行一段代码时,只有这段代码可以访问该资源。这可以防止两个线程同时访问一个资源,这可能会导致问题。
This is not restricted to only one thread. A semaphore can be configured to allow a fixed number of threads to access a resource.
这不仅限于一个线程。信号量可以配置为允许固定数量的线程访问资源。
回答by shodanex
Semaphore can also be used as a ... semaphore. For example if you have multiple process enqueuing data to a queue, and only one task consuming data from the queue. If you don't want your consuming task to constantly poll the queue for available data, you can use semaphore.
信号量也可以用作...信号量。例如,如果您有多个进程将数据排入队列,并且只有一个任务使用队列中的数据。如果您不希望您的消费任务不断轮询队列以获取可用数据,您可以使用信号量。
Here the semaphore is not used as an exclusion mechanism, but as a signaling mechanism. The consuming task is waiting on the semaphore The producing task are posting on the semaphore.
这里信号量不是用作排除机制,而是用作信号机制。消费任务正在等待信号量 生产任务正在信号量上发布。
This way the consuming task is running when and only when there is data to be dequeued
这样消费任务在且仅当有数据要出队时才运行
回答by aspen100
There are two essential concepts to building concurrent programs - synchronization and mutual exclusion. We will see how these two types of locks (semaphores are more generally a kind of locking mechanism) help us achieve synchronization and mutual exclusion.
构建并发程序有两个基本概念——同步和互斥。我们将看到这两种类型的锁(信号量更一般地是一种锁定机制)如何帮助我们实现同步和互斥。
A semaphore is a programming construct that helps us achieve concurrency, by implementing both synchronization and mutual exclusion. Semaphores are of two types, Binary and Counting.
信号量是一种编程结构,它通过实现同步和互斥来帮助我们实现并发。信号量有两种类型,二进制和计数。
A semaphore has two parts : a counter, and a list of tasks waiting to access a particular resource. A semaphore performs two operations : wait (P) [this is like acquiring a lock], and release (V)[ similar to releasing a lock] - these are the only two operations that one can perform on a semaphore. In a binary semaphore, the counter logically goes between 0 and 1. You can think of it as being similar to a lock with two values : open/closed. A counting semaphore has multiple values for count.
信号量有两部分:计数器和等待访问特定资源的任务列表。信号量执行两个操作:等待 (P) [这就像获取锁] 和释放 (V) [类似于释放锁] - 这是唯一可以对信号量执行的两个操作。在二进制信号量中,计数器在逻辑上介于 0 和 1 之间。您可以将其视为具有两个值的锁:打开/关闭。计数信号量有多个计数值。
What is important to understand is that the semaphore counter keeps track of the number of tasks that do not have to block, i.e., they can make progress. Tasks block, and add themselves to the semaphore's list only when the counter is zero. Therefore, a task gets added to the list in the P() routine if it cannot progress, and "freed" using the V() routine.
重要的是要理解信号量计数器跟踪不必阻塞的任务的数量,即它们可以取得进展。任务阻塞,并且仅当计数器为零时才将自己添加到信号量的列表中。因此,如果任务无法进行,则将其添加到 P() 例程中的列表中,并使用 V() 例程“释放”。
Now, it is fairly obvious to see how binary semaphores can be used to solve synchronization and mutual exclusion - they are essentially locks.
现在,很明显可以看到如何使用二进制信号量来解决同步和互斥问题——它们本质上是锁。
ex. Synchronization:
前任。同步:
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);
}
In the above example, B2 can only execute after B1 has finished execution. Let's say thread A comes executes first - gets to sem.P(), and waits, since the counter is 0 (closed). Thread B comes along, finishes B1, and then frees thread A - which then completes B2. So we achieve synchronization.
在上面的例子中,B2 只能在 B1 执行完毕后才能执行。假设线程 A 首先执行 - 到达 sem.P(),然后等待,因为计数器为 0(关闭)。线程 B 出现,完成 B1,然后释放线程 A - 然后完成 B2。这样我们就实现了同步。
Now let's look at mutual exclusion with a binary semaphore:
现在让我们看一下二元信号量的互斥:
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);
}
The mutual exclusion is quite simple as well - m1 and m2 cannot enter the critical section at the same time. So each thread is using the same semaphore to provide mutual exclusion for its two critical sections. Now, is it possible to have greater concurrency? Depends on the critical sections. (Think about how else one could use semaphores to achieve mutual exclusion.. hint hint : do i necessarily only need to use one semaphore?)
互斥也很简单——m1 和m2 不能同时进入临界区。因此,每个线程都使用相同的信号量为其两个临界区提供互斥。现在,是否有可能有更大的并发性?取决于临界区。(想想还有什么办法可以使用信号量来实现互斥。提示提示:我一定只需要使用一个信号量吗?)
Counting semaphore: A semaphore with more than one value. Let's look at what this is implying - a lock with more than one value?? So open, closed, and ...hmm. Of what use is a multi-stage-lock in mutual exclusion or synchronization?
计数信号量:具有多个值的信号量。让我们看看这意味着什么 - 具有多个值的锁?如此开放,关闭,然后......嗯。互斥或同步中的多级锁有什么用?
Let's take the easier of the two:
让我们从两者中更容易一点:
Synchronization using a counting semaphore: Let's say you have 3 tasks - #1 and 2 you want executed after 3. How would you design your synchronization?
使用计数信号量的同步:假设您有 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();
}
So if your semaphore starts off closed, you ensure that t1 and t2 block, get added to the semaphore's list. Then along comes all important t3, finishes its business and frees t1 and t2. What order are they freed in? Depends on the implementation of the semaphore's list. Could be FIFO, could be based some particular priority,etc. (Note : think about how you would arrange your P's and V;s if you wanted t1 and t2 to be executed in some particular order, and if you weren't aware of the implementation of the semaphore)
因此,如果您的信号量开始关闭,请确保 t1 和 t2 块被添加到信号量的列表中。然后是所有重要的 t3,完成其业务并释放 t1 和 t2。他们以什么顺序被释放?取决于信号量列表的实现。可能是先进先出,可能基于某些特定的优先级等。(注意:如果您希望 t1 和 t2 以某种特定顺序执行,并且您不知道信号量的实现,请考虑如何安排 P 和 V;s)
(Find out : What happens if the number of V's is greater than the number of P's?)
(找出:如果 V 的数量大于 P 的数量,会发生什么?)
Mutual Exclusion Using counting semaphores: I'd like you to construct your own pseudocode for this (makes you understand things better!) - but the fundamental concept is this : a counting semaphore of counter = N allows N tasks to enter the critical section freely. What this means is you have N tasks (or threads, if you like) enter the critical section, but the N+1th task gets blocked (goes on our favorite blocked-task list), and only is let through when somebody V's the semaphore at least once. So the semaphore counter, instead of swinging between 0 and 1, now goes between 0 and N, allowing N tasks to freely enter and exit, blocking nobody!
互斥使用计数信号量:我希望你为此构建自己的伪代码(让你更好地理解事情!) - 但基本概念是这样的:计数器 = N 的计数信号量允许 N 个任务自由进入临界区. 这意味着你有 N 个任务(或线程,如果你喜欢)进入临界区,但第 N+1 个任务被阻塞(进入我们最喜欢的阻塞任务列表),只有当有人 V 是信号量时才允许通过至少一次。所以信号量计数器,不是在 0 和 1 之间摆动,现在在 0 和 N 之间,允许 N 个任务自由进入和退出,不会阻塞任何人!
Now gosh, why would you need such a stupid thing? Isn't the whole point of mutual exclusion to not let more than one guy access a resource?? (Hint Hint...You don't always only have one drive in your computer, do you...?)
天哪,你为什么需要这么愚蠢的东西?难道互斥的全部意义不是让多个人访问资源吗?(提示 提示……您的计算机中并不总是只有一个驱动器,是吗……?)
To think about: Is mutual exclusion achieved by having a counting semaphore alone? What if you have 10 instances of a resource, and 10 threads come in (through the counting semaphore) and try to use the first instance?
想一想:是否仅靠计数信号量就可以实现互斥?如果您有 10 个资源实例,并且有 10 个线程进入(通过计数信号量)并尝试使用第一个实例怎么办?
回答by Volodymyr
I've created the visualization which should help to understand the idea. Semaphore controls access to a common resource in a multithreading environment.
我已经创建了应该有助于理解这个想法的可视化。信号量控制对多线程环境中公共资源的访问。
ExecutorService executor = Executors.newFixedThreadPool(7);
Semaphore semaphore = new Semaphore(4);
Runnable longRunningTask = () -> {
boolean permit = false;
try {
permit = semaphore.tryAcquire(1, TimeUnit.SECONDS);
if (permit) {
System.out.println("Semaphore acquired");
Thread.sleep(5);
} else {
System.out.println("Could not acquire semaphore");
}
} catch (InterruptedException e) {
throw new IllegalStateException(e);
} finally {
if (permit) {
semaphore.release();
}
}
};
// execute tasks
for (int j = 0; j < 10; j++) {
executor.submit(longRunningTask);
}
executor.shutdown();
Output
输出
Semaphore acquired
Semaphore acquired
Semaphore acquired
Semaphore acquired
Could not acquire semaphore
Could not acquire semaphore
Could not acquire semaphore
Sample code from the article
文章中的示例代码
回答by mweerden
A semaphore is an object containing a natural number (i.e. a integer greater or equal to zero) on which two modifying operations are defined. One operation, V
, adds 1 to the natural. The other operation, P
, decreases the natural number by 1. Both activities are atomic (i.e. no other operation can be executed at the same time as a V
or a P
).
信号量是一个包含自然数(即大于或等于零的整数)的对象,在该对象上定义了两个修改操作。一个操作,V
,将自然加 1。另一个操作P
将自然数减 1。这两个活动都是原子的(即不能与 aV
或 a同时执行其他操作P
)。
Because the natural number 0 cannot be decreased, calling P
on a semaphore containing a 0 will block the execution of the calling process(/thread) until some moment at which the number is no longer 0 and P
can be successfully (and atomically) executed.
由于自然数 0 不能减少,因此调用P
包含 0 的信号量将阻止调用进程(/线程)的执行,直到数字不再为 0 并且P
可以成功(和原子地)执行的某个时刻。
As mentioned in other answers, semaphores can be used to restrict access to a certain resource to a maximum (but variable) number of processes.
正如其他答案中提到的,信号量可用于将对特定资源的访问限制为最大(但可变)数量的进程。
回答by MABUTOBRIGHTON
A hardware or software flag. In multi tasking systems , a semaphore is as variable with a value that indicates the status of a common resource.A process needing the resource checks the semaphore to determine the resources status and then decides how to proceed.
硬件或软件标志。在多任务系统中,信号量作为变量,其值指示公共资源的状态。需要资源的进程检查信号量以确定资源状态,然后决定如何进行。