多线程:Signal vs BusyWait(Polling),线程间条件变量问题
Posted
技术标签:
【中文标题】多线程:Signal vs BusyWait(Polling),线程间条件变量问题【英文标题】:Multi-threading: Signal vs BusyWait(Polling), Inter-thread condition variables questions 【发布时间】:2009-06-04 12:41:57 【问题描述】:我有一个关于线程间通信的一般性问题。
现在我正在使用一堆 C++ 线程 (~15)。
他们都在使用 BusyWait(轮询)彼此来获取要处理的数据。但是很难保持较低的 CPU 使用率 && 提供良好的性能并避免进行过多的上下文切换。
所以我正在查看条件变量和信号。我想我理解线程进入 .Wait()、等待另一个线程调用 .Signal() 的一般概念。
问题 #1)我的问题可能是概念性的,但如果等待信号的线程在等待时被暂停,它自己无法执行任何操作。有没有办法让它自己醒来执行一些动作。
问题 #2)此外,我的课程用于双向传递数据。但是如果中间类正在等待另一个类的信号,它就无法向那个类发送信号。如:
_________ _________ __________
| Class A |---newData Signal--->| Class B |---newData Signal--->| Class C |
| | |(WAITING)|<---newData Signal---| |
--------- --------- ----------
因此,如果 B 类为来自 C 的 .Signal() 开启 .Wait(),则它无法处理来自 A 的新信号。
是否有可能两个 A && C 发送相同的“newData”信号 B 来唤醒它?是否可以区分来自 A && C 的信号。
我正在使用 C++ 使用 ACE 框架进行编码,并且可能会切换到 Boost。但我想这足够通用,我可以将答案应用于任何操作系统(希望如此)。
谢谢
【问题讨论】:
【参考方案1】:如果你想让你的父线程在子线程运行时工作,你可以等待一个超时信号。每次超时到期时,您都会做一些工作并再次等待。
【讨论】:
【参考方案2】:问题 #1)在大多数实现中,您可以限制最大等待时间,因此说:等待 2 秒,然后做某事并再次等待。
问题 #2) 在大多数实现中,您可以一次等待多个信号。你可以说:如果信号 A 或 B 被触发就唤醒。
【讨论】:
你能举例说明函数等待超过 on 信号吗?我看过 Boost 并没有看到任何这样的东西。仅在 Windows 一侧似乎有 WaitForMultipleObject,但我不确定这是否是您在说的……不确定 Linux…… @xcimo:是的,WaitForMultipleObject 就是我在 Windows 中提到的。不幸的是,我不知道 boost 能给你带来什么(我们使用了一个手写的跨平台类,它在内部使用了 Windows 的 WaitForMultipleObject 和 Linux 上的 pthread 条件变量周围的复杂代码)。【参考方案3】:您寻求的答案非常复杂,而且这个 wiki 上的空间还不足以解决所有问题:(
您需要做的是为自己找到一些很好的网站,这些网站可以解释线程是如何工作的。您所追求的大部分内容都可以通过正确的设计来完成,但您首先需要更好地理解这些概念。
为了让您的通信顺利进行,您需要将信号发送到正确的位置并等待正确的事件。
最简单的方法是使用所有线程共享的单个条件变量,这将使您获得比轮询更好的效果。当这种情况发出信号时,他们都会醒来并寻找一些工作要做。
这效率不高,但很简单,对你有用,而且比轮询更有效。一旦你完成了这项工作,你可以尝试引入一些新的条件变量并拆分哪些线程等待哪些线程——这样做时你会犯很多错误并遇到很多死锁和饥饿。坚持下去,你就会开始明白这一切是如何运作的。
祝你好运。
【讨论】:
【参考方案4】:虽然您可以为此使用条件变量,但问题描述建议改用消息队列。然后,线程 A 和线程 C 可以将消息注入到 B 的队列中,B 进行相应的处理。 (当然,为了区分两个线程,你应该安排A和C发送不同的消息。)
我不知道 ACE 对消息队列有什么支持,但是,在(比如说)Java 并发框架中,您可以使用 ConcurrentLinkedQueue
构建自己的穷人消息队列。 :-)
【讨论】:
我所有的线程/类都已经有队列,在 ACE 中它们被称为 ACE_Unbounded_Queue。所以这不是问题。【参考方案5】:假设您有某种方法可以进行临界区锁定(例如 java 同步)或线程安全队列,您可以使用运行队列。
对于每个线程,修改/覆盖睡眠实现,以便当线程等待时,它将自己添加到运行队列的末尾。
假设一次只应该运行一个线程,当前运行的线程在它自己进入睡眠/等待之前应该做的最后一件事是唤醒列表头部的线程。
如果您需要更复杂的线程执行/调度,下一步是创建一个遍历队列的调度程序线程,调整队列中线程的顺序,检查哪个线程拥有它需要的所有资源运行等
【讨论】:
以上是关于多线程:Signal vs BusyWait(Polling),线程间条件变量问题的主要内容,如果未能解决你的问题,请参考以下文章
多线程编程中条件变量和的spurious wakeup 虚假唤醒
java 多线程 22 :生产者/消费者模式 进阶 利用await()/signal()实现