multithreading 使用条件变量而不是互斥锁的优点

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4742196/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 01:12:37  来源:igfitidea点击:

Advantages of using condition variables over mutex

multithreadingvariablespthreadsmutexconditional-statements

提问by Abhi

I was wondering what is the performance benefit of using condition variables over mutex locks in pthreads.

我想知道在 pthread 中使用条件变量而不是互斥锁的性能优势是什么。

What I found is : "Without condition variables, the programmer would need to have threads continually polling (possibly in a critical section), to check if the condition is met. This can be very resource consuming since the thread would be continuously busy in this activity. A condition variable is a way to achieve the same goal without polling." (https://computing.llnl.gov/tutorials/pthreads)

我发现的是:“如果没有条件变量,程序员需要让线程不断轮询(可能在临界区),以检查是否满足条件。这可能非常消耗资源,因为线程会在此持续忙碌活动。条件变量是一种无需轮询即可实现相同目标的方法。” ( https://computing.llnl.gov/tutorials/pthreads)

But it also seems that mutex calls are blocking (unlike spin-locks). Hence if a thread (T1) fails to get a lock because some other thread (T2) has the lock, T1 is put to sleep by the OS, and is woken up only when T2 releases the lock and the OS gives T1 the lock. The thread T1 does not really poll to get the lock. From this description, it seems that there is no performance benefit of using condition variables. In either case, there is no polling involved. The OS anyway provides the benefit that the condition-variable paradigm can provide.

但似乎互斥调用是阻塞的(与自旋锁不同)。因此,如果一个线程 (T1) 由于其他线程 (T2) 拥有锁而未能获得锁,则 T1 将被操作系统置于睡眠状态,并且仅在 T2 释放锁且操作系统为 T1 提供锁时才被唤醒。线程 T1 并没有真正轮询以获取锁。从这个描述来看,使用条件变量似乎没有性能优势。在任何一种情况下,都不涉及轮询。无论如何,操作系统提供了条件变量范式可以提供的好处。

Can you please explain what actually happens.

你能解释一下实际发生的情况吗?

回答by Michael Burr

A condition variable allows a thread to be signaled when something of interest to that thread occurs.

条件变量允许在线程发生感兴趣的事情时向该线程发出信号。

By itself, a mutex doesn't do this.

互斥锁本身不会这样做。

If you just need mutual exclusion, then condition variables don't do anything for you. However, if you need to know when something happens, then condition variables can help.

如果您只需要互斥,那么条件变量不会为您做任何事情。但是,如果您需要知道某事何时发生,那么条件变量会有所帮助。

For example, if you have a queue of items to work on, you'll have a mutex to ensure the queue's internals are consistent when accessed by the various producer and consumer threads. However, when the queue is empty, how will a consumer thread know when something is in there for it to work on? Without something like a condition variable it would need to poll the queue, taking and releasing the mutex on each poll (otherwise a producer thread could never put something on the queue).

例如,如果您有一个项目队列要处理,您将有一个互斥锁来确保队列的内部在被各种生产者和消费者线程访问时保持一致。然而,当队列为空时,消费者线程如何知道什么时候有东西可以处理呢?如果没有条件变量之类的东西,它需要轮询队列,在每次轮询时获取和释放互斥锁(否则生产者线程永远不会在队列中放置任何东西)。

Using a condition variable lets the consumer find that when the queue is empty it can just wait on the condition variable indicating that the queue has had something put into it. No polling - that thread does nothing until a producer puts something in the queue, then signals the condition that the queue has a new item.

使用条件变量可以让消费者发现,当队列为空时,它可以等待条件变量表明队列已放入某些东西。无轮询 - 该线程不执行任何操作,直到生产者将某些内容放入队列中,然后通知队列有新项目的条件。

回答by seh

You're looking for too much overlap in two separate but related things: a mutex and a condition variable.

您在两个独立但相关的事物中寻找太多重叠:互斥锁和条件变量。

A common implementation approach for a mutex is to use a flag and a queue. The flag indicates whether the mutex is held by anyone (a single-count semaphore would work too), and the queue tracks which threads are in line waiting to acquire the mutex exclusively.

互斥锁的常见实现方法是使用标志和队列。该标志指示互斥是否由任何人持有(单计数信号量也可以工作),并且队列跟踪哪些线程正在排队等待以独占方式获取互斥。

A condition variable is then implemented as another queue bolted onto that mutex. Threads that got in line to wait to acquire the mutex can—usually once they have acquired it—volunteer to get out of the front of the line and get into the condition queue instead. At this point, you have two separate sets of waiters:

然后将条件变量实现为栓接到该互斥锁上的另一个队列。排队等待获取互斥锁的线程可以——通常是一旦他们获取了它——自愿离开队列的前面并进入条件队列。此时,您有两组独立的服务员:

  • Those waiting to acquire the mutex exclusively
  • Those waiting for the condition variable to be signaled
  • 那些等待获得互斥锁的人
  • 那些等待条件变量发出信号的

When a thread holding the mutex exclusively signals the condition variable, for which we'll assume for now that it's a singular signal(unleashing no more than one waiting thread) and not a broadcast(unleashing all the waiting threads), the first thread in the condition variable queue gets shunted back over into the front (usually) of the mutex queue. Once the thread currently holding the mutex—usually the thread that signaled the condition variable—relinquishes the mutex, the next thread in the mutex queue can acquire it. That next thread in line will have been the one that was at the head of the condition variable queue.

当一个持有互斥锁的线程专门向条件变量发出信号时,我们现在假设它是一个单一的信号(释放不超过一个等待线程)而不是广播(释放所有等待线程),第一个线程条件变量队列被分流回互斥队列的前面(通常)。一旦当前持有互斥量的线程——通常是发出条件变量信号的线程——放弃互斥量,互斥量队列中的下一个线程就可以获取它。队列中的下一个线程将是位于条件变量队列头部的线程。

There are many complicated details that come into play, but this sketch should give you a feel for the structures and operations in play.

有许多复杂的细节在起作用,但是这个草图应该让你对游戏中的结构和操作有一个感觉。

回答by johnnycrash

If you are looking for performance, then start reading about "non blocking / non locking" thread synchronization algorithms. They are based upon atomic operations, which gccis kind enough to provide. Lookup gcc atomic operations. Our tests showed we could increment a global value with multiple threads using atomic operation magnitudes faster than locking with a mutex. Here is some sample code that shows how to add items to and from a linked list from multiple threads at the same time without locking.

如果您正在寻找性能,那么请开始阅读“非阻塞/非锁定”线程同步算法。它们基于原子操作,这gcc足以提供。查找 gcc 原子操作。我们的测试表明,与使用互斥锁锁定相比,我们可以使用原子操作幅度更快地使用多个线程增加全局值。 下面是一些示例代码,展示了如何在不锁定的情况下同时从多个线程向链表添加项目和从链表添加项目。

For sleeping and waking threads, signals are much faster than conditions. You use pthread_killto send the signal, and sigwaitto sleep the thread. We tested this too with the same kind of performance benefits. Here is some example code.

对于休眠和唤醒线程,信号比条件快得多。你pthread_kill用来发送信号,并sigwait让线程休眠。我们也用同样的性能优势对此进行了测试。 这是一些示例代码。