【问题标题】:Advantages of using condition variables over mutex使用条件变量优于互斥锁的优点
【发布时间】:2011-06-12 03:33:27
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: multithreading variables pthreads mutex conditional-statements


    【解决方案1】:

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

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

    然后将条件变量实现为栓在该互斥体上的另一个队列。排队等待获取互斥锁的线程可以(通常在获取互斥锁后)自愿退出队列并进入条件队列。此时,您有两组独立的服务员:

    • 等待独占获取互斥锁的用户
    • 等待条件变量发出信号的那些

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

    有许多复杂的细节会发挥作用,但这个草图应该让您对游戏中的结构和操作有所了解。

    【讨论】:

    • pthreads 互斥锁和条件变量更简单——没有队列,只有一组无序的等待进程。
    • 当需要在 signal 案例中选择“下一个”时,其中一个将是“第一个”,因此该集合必须投影到序列中。我知道没有 FIFO 保证,但当然可以构建一个互斥/条件对来保证该保证。见证 Win32 的各种用户空间“公平”实现。
    • 谢谢!我理解条件变量并不是真正实现互斥锁的另一种方式的基本思想,但它们提供了一种信号结构来更好地管理竞争线程之间的锁分布。..
    【解决方案2】:

    如果您正在寻找性能,那么请开始阅读“非阻塞/非锁定”线程同步算法。它们基于原子操作,gcc 可以提供。查找 gcc 原子操作。我们的测试表明,我们可以使用原子操作幅度比使用互斥锁更快地增加多个线程的全局值。 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.

    对于睡眠和唤醒线程,信号比条件快得多。您使用pthread_kill 发送信号,使用sigwait 休眠线程。我们也以同样的性能优势对此进行了测试。 Here is some example code.

    【讨论】:

    • 非常感谢您指出非锁定同步领域!我现在正在调查!
    • 关于使用信号而不是条件变量的好建议。
    【解决方案3】:

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

    互斥锁本身不会这样做。

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

    例如,如果您有一个要处理的项目队列,那么您将拥有一个互斥锁来确保队列的内部结构在被各种生产者和消费者线程访问时是一致的。然而,当队列为空时,消费者线程如何知道什么时候有东西在那里工作呢?如果没有条件变量之类的东西,它将需要轮询队列,在每次轮询时获取和释放互斥锁(否则生产者线程永远无法将某些内容放入队列中)。

    使用条件变量可以让消费者发现,当队列为空时,它可以等待条件变量,表明队列已经放入了一些东西。无轮询 - 该线程什么都不做,直到生产者将某些东西放入队列中,然后发出队列有新项目的信号。

    【讨论】:

    • 对。互斥锁只允许你等到锁可用;条件变量允许您等到某些应用程序定义的条件发生变化。
    • 谢谢你的例子!收拾东西了:)
    • 我不明白为什么你不能删除条件变量,而只在一些简单的情况下使用互斥锁。使获取互斥锁成为“工作可用”条件。然后让生产者线程获取互斥体,工作线程尝试获取它。当工作可用时,生产者解锁互斥锁。然后操作系统将使用获取的互斥锁唤醒工作人员,工作人员将运行完成,然后通过尝试重新获取自己的互斥锁来休眠(死锁本身)。一旦生产者有更多的工作,它可以解锁互斥锁。可能需要先检查互斥体是否已锁定。
    • 假设一个非递归互斥锁(同一个线程不能多次锁定),@Eloff,你的想法会死锁一切,而不仅仅是消费者线程(“它自己”)。生产者线程无法解锁它尚未锁定的互斥锁,因此它将永远卡住等待消费者解锁互斥锁,它永远不会因为它试图重新锁定它已经锁定的互斥锁。跨度>
    猜你喜欢
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 1970-01-01
    • 2015-11-17
    • 1970-01-01
    相关资源
    最近更新 更多