2011-01-20 68 views
15

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

我发现的是:“没有条件变量,程序员需要有线程不断轮询(可能在临界段),以检查是否满足条件,这是非常消耗资源的,因为线程会。在这项活动中不断忙碌,条件变量是一种不用投票就能达到同样目标的方法。“ (https://computing.llnl.gov/tutorials/pthreads

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

你能解释一下究竟发生了什么。

回答

32

条件变量允许在感兴趣的东西该线程发生时信号的线程。

就其本身而言,一个互斥体并没有这样做。

如果您只需要相互排斥,那么条件变量不为你做任何事情。但是,如果您需要知道什么时候发生,那么条件变量可以提供帮助。

例如,如果您有一个要处理的项目队列,则会有一个互斥锁,以确保队列的内部在由各个生产者线程和使用者线程访问时保持一致。但是,当队列为空时,消费者线程如何知道什么时候有内容处理它?如果没有像条件变量那样的东西,它需要轮询队列,在每次轮询时采取和释放互斥量(否则生产者线程永远不能在队列中放置某些东西)。

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

+5

对。一个互斥锁只允许你等待,直到锁定可用;一个条件变量允许您等待某个应用程序定义的条件发生变化。 – caf 2011-01-20 05:09:51

+0

谢谢你的例子!清理的东西:) – Abhi 2011-01-20 18:21:59

+6

我不明白为什么你不能放下条件变量,只是在一些简单的情况下使用互斥。获取“工作可用”条件下的互斥锁。然后让生产者线程获得互斥量,并且工作线程尝试获取它。当工作可用时,生产者解锁互斥体。然后操作系统会通过获取互斥锁来唤醒工作人员,然后工作人员将运行到完成状态,然后尝试重新获取它自己的互斥锁,然后进入睡眠状态(死锁本身)。一旦生产者有更多的工作,它可以解锁互斥。可能需要小心检查互斥锁是否先被锁定。 – Eloff 2013-04-06 20:34:14

4

你在两个不同但相关的事情找太多的重叠:互斥和条件变量。

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

条件变量然后作为螺栓固定到该互斥另一个队列来实现。线程等待获取互斥量的线程通常可以在获得互斥后通过—自动退出线路并进入条件队列。在这一点上,你有两个独立的一组服务员:

  • 那些正在等待获取互斥专门当一个线程持有互斥专门
  • 那些等待条件变量发出信号

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

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

5

如果您正在寻找性能,那么请阅读“非阻塞/非锁定”线程同步算法。它们基于原子操作,其中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.