在什么情况下你会在 C++ 中的互斥体上使用信号量?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2350544/
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
In what situation do you use a semaphore over a mutex in C++?
提问by jasonline
Throughout the resources I've read about multithreading, mutex is more often used and discussed compared to a semaphore. My question is when do you use a semaphore over a mutex? I don't see semaphores in Boost thread. Does that mean semaphores no longer used much these days?
在我阅读的有关多线程的所有资源中,与信号量相比,互斥锁更常被使用和讨论。我的问题是你什么时候在互斥锁上使用信号量?我在 Boost 线程中没有看到信号量。这是否意味着现在信号量不再使用太多?
As far as I've understand, semaphores allow a resource to be shared by several threads. This is only possible if those threads are only reading the resource but not writing. Is this correct?
据我所知,信号量允许多个线程共享一个资源。仅当这些线程仅读取资源而不写入时才有可能。这样对吗?
采纳答案by Steve Jessop
Boost.Thread has mutexes and condition variables. Purely in terms of functionality, semaphores are therefore redundant[*], although I don't know if that's why they're omitted.
Boost.Thread 有互斥体和条件变量。纯粹就功能而言,信号量因此是多余的[*],尽管我不知道这是否就是它们被省略的原因。
Semaphores are a more basic primitive, simpler, and possibly implemented to be faster, but don't have priority-inversion avoidance. They're arguably harder to use than condition variables, because they require the client code to ensure that the number of posts "matches" the number of waits in some appropriate way. With condition variables it's easy to tolerate spurious posts, because nobody actually doesanything without checking the condition.
信号量是一种更基本的原语,更简单,并且可能实现得更快,但没有优先级反转避免。它们可以说比条件变量更难使用,因为它们需要客户端代码以某种适当的方式确保帖子数量“匹配”等待数量。使用条件变量很容易容忍虚假帖子,因为实际上没有人不检查条件就做任何事情。
Read vs. write resources is a red herring IMO, it has nothing to do with the difference between a mutex and a semaphore. If you use a counting semaphore, you could have a situation where multiple threads are concurrently accessing the same resource, in which case it would presumably have to be read-only access. In that situation, you might be able to use shared_mutex
from Boost.Thread instead. But semaphores aren't "for" protecting resources in the way mutexes are, they're "for" sending a signal from one thread to another. It's possible to usethem to control access to a resource.
读与写资源是一个红鲱鱼 IMO,它与互斥锁和信号量之间的区别无关。如果您使用计数信号量,您可能会遇到多个线程同时访问同一资源的情况,在这种情况下,它可能必须是只读访问。在这种情况下,您可能可以使用shared_mutex
from Boost.Thread 代替。但是信号量并不是像互斥锁那样“为了”保护资源,而是“为了”从一个线程向另一个线程发送信号。可以使用它们来控制对资源的访问。
That doesn't mean that all uses of semaphores must relate to read-only resources. For example, you can use a binary semaphore to protect a read/write resource. Might not be a good idea, though, since a mutex often gives you better scheduling behaviour.
这并不意味着信号量的所有使用都必须与只读资源相关。例如,您可以使用二进制信号量来保护读/写资源。不过,这可能不是一个好主意,因为互斥锁通常可以为您提供更好的调度行为。
[*] Here's roughly how you implement a counting semaphore using a mutex and a condition variable. To implement a shared semaphore of course you need a shared mutex/condvar:
[*] 这里大致介绍了如何使用互斥锁和条件变量来实现计数信号量。要实现共享信号量,您当然需要共享互斥锁/condvar:
struct sem {
mutex m;
condvar cv;
unsigned int count;
};
sem_init(s, value)
mutex_init(s.m);
condvar_init(s.cv);
count = value;
sem_wait(s)
mutex_lock(s.m);
while (s.count <= 0) {
condvar_wait(s.cv, s.m);
}
--s.count;
mutex_unlock(s.m);
sem_post(s)
mutex_lock(s.m);
++s.count;
condvar_broadcast(s.cv)
mutex_unlock(s.m);
Therefore, anything you can do with semaphores, you can do with mutexes and condition variables. Not necessarily by actually implementing a semaphore, though.
因此,你可以用信号量做的任何事情,你都可以用互斥体和条件变量来做。不过,不一定要实际实现信号量。
回答by Niki
The typical use case for a mutex (allowing only one thread access to a resource at any time) is far more common than the typical uses if a semaphore. But a semaphore is actually the more general concept: A mutex is (almost) a special case of a semaphore.
互斥锁的典型用例(在任何时候只允许一个线程访问资源)比信号量的典型用例更常见。但信号量实际上是更一般的概念:互斥锁(几乎)是信号量的特例。
Typical applications would be: You don't want to create more than (e.g.) 5 database connections. No matter how many worker threads there are, they have to share these 5 connections. Or, if you run on a N-core machine, you might want to make sure that certain CPU/memory-intensive tasks don't run in more than N threads at the same time (because that would only reduce throughput due to context switches and cache thrashing effects). You might even want to limit the number of parallel CPU/memory intensive tasks to N-1, so the rest of the system doesn't starve. Or imagine a certain task needs a lot of memory, so running more than N instances of that task at the same time would lead to paging. You could use a semaphore here, to make sure that no more than N instances of this particular task run at the same time.
典型的应用程序是: 您不想创建超过(例如)5 个数据库连接。无论有多少工作线程,它们都必须共享这5个连接。或者,如果您在 N 核机器上运行,您可能希望确保某些 CPU/内存密集型任务不会同时在超过 N 个线程中运行(因为这只会由于上下文切换而降低吞吐量和缓存抖动效果)。您甚至可能希望将并行 CPU/内存密集型任务的数量限制为 N-1,这样系统的其余部分就不会饿死。或者想象某个任务需要大量内存,因此同时运行该任务的 N 个以上实例会导致分页。您可以在此处使用信号量,以确保同时运行此特定任务的实例不超过 N 个。
EDIT/PS: From your question "This is only possible if those threads are only reading the resource but not writing. Is this correct?" and your comment, it seems to me as if you're thinking of a resource as a variable or a stream, that can be read or written and that can only be written to by one thread at a time. Don't. This is misleading in this context.
编辑/ PS:从你的问题“这只有在这些线程只读取资源而不是写入时才有可能。这是正确的吗?” 和您的评论,在我看来,好像您将资源视为变量或流,可以读取或写入,并且一次只能由一个线程写入。别。在这种情况下,这是一种误导。
Think of resources like "water". You can use water to wash your dishes. I can use water to wash my dishes at the same time. We don't need any kind of synchronization for that, because there is enough water for both of us. We don't necessarily use the samewater. (And you can't "read" or "write" water.) But the total amountof water is finite. So it's not possible for anynumber of parties to wash their dishes at the same time. This kind of synchronization is done with a semaphore. Only usually not with water but with other finite resources like memory, disk space, IO throughput or CPU cores.
把资源想象成“水”。你可以用水洗碗。我可以同时用水洗碗。我们不需要任何同步,因为我们俩都有足够的水。我们不一定使用相同的水。(而且你不能“读”或“写”水。)但是水的总量是有限的。因此,任何数量的聚会都不可能同时洗碗。这种同步是通过信号量完成的。只是通常不使用水,而是使用其他有限资源,如内存、磁盘空间、IO 吞吐量或 CPU 内核。
回答by Tall Jeff
The essence of the difference between a mutex and a semaphore has to do with the concept of ownership. When a mutex is taken, we think of that thread as owning the mutex and that same thread must later release the mutex back to release the resource.
互斥体和信号量之间区别的本质与所有权的概念有关。当一个互斥锁被占用时,我们认为该线程拥有互斥锁,并且该线程稍后必须释放互斥锁以释放资源。
For a semaphore, think of taking the semaphore as consuming the resource, but not actually taking ownership of it. This is generally referred to as the semaphore being "empty" rather than owned by a thread. The feature of the semaphore is then that a different thread can "fill" the semaphore back to "full" state.
对于信号量,可以将信号量视为消耗资源,而不是实际获得它的所有权。这通常被称为信号量是“空的”而不是由线程拥有。信号量的特征是不同的线程可以“填充”信号量回到“完整”状态。
Therefore, mutexes are usually used for the concurrency protection of resources (ie: MUTual EXlusion) while semaphores are used for signaling between threads (like semaphore flags signaling between ships). A mutex by itself can't really be used for signaling, but semaphores can. So, selecting one over the other depends on what you are trying to do.
因此,互斥锁通常用于资源的并发保护(即:MUTual EXlusion),而信号量用于线程之间的信令(如船舶之间的信号量标志)。互斥体本身不能真正用于信号传递,但信号量可以。因此,选择其中之一取决于您要尝试做什么。
See another one of my answers herefor more discussion on a related topic covering the distinction between recursive and non-recursive mutexes.
回答by R Samuel Klatchko
To control access to a limited number of resources being shared by multiple threads (either inter- or intra-process).
控制对由多个线程(进程间或进程内)共享的有限数量资源的访问。
In our application, we had a very heavy resource and that we did not want to allocate one for each of the M worker threads. Since a worker thread needed the resource for just one small part of their job, we rarely were using more then a couple of the resources simultaneously.
在我们的应用程序中,我们有一个非常繁重的资源,我们不想为 M 个工作线程中的每一个分配一个。由于工作线程只需要一小部分工作的资源,我们很少同时使用超过几个资源。
So, we allocated N of those resources and put them behind a semaphore initialized to N. When more then N threads were trying to use the resource, they would just block until one was available.
因此,我们分配了这些资源中的 N 个并将它们放在初始化为 N 的信号量之后。当超过 N 个线程尝试使用该资源时,它们将阻塞直到一个可用。
回答by Kiril
I feel like there is no simple way to REALLYanswer your question without disregarding some important information about semaphores. People have written many books about semaphores, so any one or two paragraph answer is a disservice. A popular book is The Little Book of Semaphores... for those who don't like big books :).
我觉得没有简单的方法可以在不忽略有关信号量的一些重要信息的情况下真正回答您的问题。人们已经写了很多关于信号量的书,所以任何一两段的回答都是一种伤害。一本很受欢迎的书是《信号量小书》……适合那些不喜欢大书的人:)。
Here is a decent lengthy articlewhich goes into a LOT of the details on how semaphores are used and how they're intended to be used.
这是一篇相当长的文章,其中详细介绍了如何使用信号量以及如何使用它们。
Update:
Dan pointed out some mistakes in my examples, I'll leave it with the references which offer MUCH better explanations than mine :).
更新:
丹在我的例子中指出了一些错误,我会把它留给参考文献,这些参考文献提供的解释比我的要好得多:)。
Here are the references showing the RIGHT ways one should use a semaphore:
1. IBM Article
2. University of Chicago Class Lecture
3. The Netrino article I originally posted.
4. The "sell tickets" paper + code.
以下是显示使用信号量的正确方式的参考资料:
1. IBM 文章
2. 芝加哥大学课堂讲座
3. 我最初发布的 Netrino 文章。
4.《卖票》纸+代码。
回答by Kane
As taken from this article:
摘自这篇文章:
A mutex allows inter-process synchronisation to occur. If you instantiate a mutex with a name (as in the code above), the mutex becomes system-wide. This is really useful if you're sharing the same library between many different applications and need to block access to a critical section of code that is accessing resources that can't be shared.
Finally, the Semaphore class. Let's say you have a method that is really CPU intensive, and also makes use of resources that you need to control access to (using Mutexes :)). You've also determined that a maximum of five calls to the method is about all your machine can hanlde without making it unresponsive. Your best solution here is to make use of the Semaphore class which allows you to limit a certain number of threads' access to a resource.
互斥锁允许发生进程间同步。如果您使用名称实例化互斥锁(如上面的代码中所示),则互斥锁将成为系统范围的。如果您在许多不同的应用程序之间共享同一个库,并且需要阻止对正在访问无法共享的资源的关键代码部分的访问,这非常有用。
最后,信号量类。假设您有一个真正占用 CPU 的方法,并且还利用了您需要控制访问的资源(使用互斥体 :))。您还确定了对该方法的最多五次调用是您的机器可以处理的所有内容,而不会使其无响应。这里最好的解决方案是使用 Semaphore 类,它允许您限制一定数量的线程对资源的访问。
回答by MRN
Semaphores was conceived originally for synchronization across processes. Windows uses WaitForMultipleObjects that is like a semaphore. In linux world, the initial pthread implementation did not allow a mutex to be shared across process. Now they do. The concept of breaking the atomic increment (interlocked increment in Windows) along with light weight mutex is most practical implementation these days after threads became the unit of scheduling for cpu. If the increment and the lock were together (semaphore), the time to acquire / release locks will be too long and we cannot split those 2 unit functions as we do today for performance and better synchronization constructs.
信号量最初是为跨进程同步而设计的。Windows 使用类似于信号量的 WaitForMultipleObjects。在 linux 世界中,最初的 pthread 实现不允许跨进程共享互斥锁。现在他们做到了。在线程成为 cpu 的调度单位之后,打破原子增量(Windows 中的互锁增量)以及轻量级互斥锁的概念是当今最实用的实现。如果增量和锁在一起(信号量),获取/释放锁的时间将太长,我们不能像今天那样拆分这两个单元函数以提高性能和更好的同步结构。
回答by seas
As far as I understand semaphores is a strongly IPC-related term these days. It still means protected variable many processes can modify, but among processes and this feature is supported by OS.
据我所知,如今信号量是一个与 IPC 密切相关的术语。这仍然意味着许多进程可以修改受保护的变量,但是在进程之间,操作系统支持此功能。
Usually, we don't need a variable and a simple mutex cover all our requirements. If we still need a variable, probably, we code it ourselves - "variable + mutex" to get more control.
通常,我们不需要变量,一个简单的互斥锁就可以满足我们的所有要求。如果我们仍然需要一个变量,可能,我们自己编码 - “变量 + 互斥锁”以获得更多控制。
Resume: we don't use semaphores in multithreading because usually use mutex for simplicity and control, and we use semaphores for IPC because it's OS-supported and an official name for processes synchronization mechanism.
简历:我们在多线程中不使用信号量,因为通常使用互斥量来简化和控制,我们在 IPC 中使用信号量,因为它是操作系统支持的,并且是进程同步机制的正式名称。
回答by Kousha
From what I learned about semaphores and mutex's in college, semaphore are more theoretical objects while mutex's are one implementation of semaphores. Taking that into account, semaphores are more flexible.
从我在大学学到的关于信号量和互斥量的知识,信号量是更多的理论对象,而互斥量是信号量的一种实现。考虑到这一点,信号量更加灵活。
Mutex's are highly implementation dependent. They have been optimized for their binary locking purpose. The normal use case of a mutex is a binary semaphore.
互斥体高度依赖于实现。它们已针对其二进制锁定目的进行了优化。互斥锁的正常用例是二进制信号量。
In general, when trying to write bug-free multithreaded code, simplicity helps. Mutex's are used more because their simplicity helps avoid complex deadlock scenarios that arise from using semaphores.
通常,在尝试编写无错误的多线程代码时,简单性会有所帮助。Mutex 的使用较多,因为它们的简单性有助于避免因使用信号量而产生的复杂死锁场景。