如何向多个线程发出事件信号
如何将事件发送到应用程序的多个正在运行的线程?
例如:我的主线程想要向所有正在运行的线程发出应用程序将退出的信号。
我有点困惑这些可能性中的哪一种会带来简单可靠的解决方案:
condition_variable
和notify_all
fromboost
CONDITION_VARIABLE
code> 和来自 WinApi 的WakeAllConditionsVariable
CEvent
来自 WinApi,- 在线程上使用
mutex
和try_lock
- 来自
boost
的signal
和slots
- ,甚至是带有
CriticalSection
的全局变量,用于保护 getter/setter - 在此处插入您的解决方案...
您能否为初学者指明正确的方向,并且也许可以对我提供的上述可能性进行一些说明。
补充: - 操作系统是Windows XP
How can I send an event to several running threads of my application?
For example: My main thread wants to signal all running threads that the application will exit.
I'm a little confused which of those possibilities leads to an easy and reliable solution:
condition_variable
withnotify_all
fromboost
CONDITION_VARIABLE
withWakeAllConditionsVariable
from WinApiCEvent
from WinApi- using a
mutex
andtry_lock
on the threads signal
andslots
fromboost
- or even a global variable with a
CriticalSection
for protection of getter/setters - insert your solution here...
Could you point a beginner into the right direction and perhaps loose some words about the possiblities above I provided.
Added: - OS is Windows XP
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
发布评论
评论(4)
你在这里有两个不同的问题。第一个是如何使代码的不同部分可以使用一点数据,以便不同的线程可以读取它,而第二个是如何以线程安全的方式修改一点状态,以便线程可以读取它。采取行动。
要使一些数据可供不同的线程使用,您可以使用不同的方法:
- 如果您有对管理线程的对象的引用,您可以使用它们来手动遍历线程列表并通知每个线程
- 您可以使用 boost::在程序中的某个时刻发出信号来管理这些引用(即创建一个
exit
信号,并在构造时连接所有线程。 - 如果您没有这些引用,则可以设置
exit< /code> 标记为全局变量并让线程从中读取
I倾向于选择每个线程由单个对象管理,在这种情况下,我会尝试将数据位移动到该管理器并避免全局。不需要信号机制,因为您可以遍历这些依赖项。
问题的第二部分是如何以线程安全的方式将数据从主线程传递到其余线程,在这种特殊情况下,一位数据在程序的大部分时间里只有一个值,并且被设置为在某一点有不同的固定值。这是全局变量还是线程管理器状态的一部分并不重要:
- 互斥体。每个线程在读/写之前锁定,确保修改是安全的
- 原子操作。对于足够小的数据类型,线程库/操作系统提供原子操作,这些操作将添加适当的内存屏障,以确保数据跨区域移动。
结合您拥有的全局变量或局部变量,将直接修改(全局变量) )或通过从主线程触发的函数调用,直接或通过某种信号机制。标志的修改必须通过原子操作(引入内存栅栏)或通过持有互斥体来完成。
如果线程可以处于非活动状态(等待条件变量),那么您可能希望避免全局、调用函数、锁定互斥体以及互斥体内部都更改标志并使用条件变量来唤醒等待线程。等待线程在从某种条件下等待返回时将负责检查该标志。
如果您只是测试简单的状态更改(何时退出),则条件变量就足够了。 只要确保找到一个好的 WinAPI 示例即可:您几乎肯定需要多次测试状态(与您的直觉可能告诉您的不同)。
如果您打算在线程之间共享任务/事件,好的模式是使用基于互斥队列和条件变量的生产者-消费者模式。一个典型的例子是,您有一个生产者线程,它生成的作业被推送到队列中(例如,每个任务将数据写入不同的文件)。然后,您可以拥有多个消费者线程,它们将从队列中删除任务并对其执行操作。要在线程之间进行通信,您可以使用一个条件变量,该变量将向使用者线程发出信号:互斥队列可能为空。 (阅读条件变量以了解为什么我在这里说“可能”)。
随着您进行越来越多的线程编程,您将寻找不需要互斥体的机会。例如,如果我在磁盘上有大量只读数据,并且我希望您的线程根据这些数据进行计算,您可以在生成线程之前读取数据。然后,所有线程都可以访问这些数据,而无需使用锁,只要它们正常运行并且不写入数据即可。您会发现仅使用互斥体和条件变量即可实现许多线程模式(POSIX 线程库如此之小是部分原因)。
最后一点:这有点超出了您的问题范围,但您也会了解真正需要生成多少线程。线程之间的上下文切换相对便宜,但成本绝对不为零。这意味着当产生更多线程时,会出现收益递减点。从操作系统的角度考虑一下:除了实际进行切换之外,算法还必须花费周期来决定何时进行上下文切换。这个成本并不是零。因此,当您进行更多线程编程时,您会发现另一种类型的优化是,何时在线程中执行尽可能多的工作,而不等待另一个线程完成工作。当然,您必须在设计的简洁性和速度优化之间取得平衡。这只是您设计应用程序时需要考虑的事情。
boost::condition_variable
预期 C++11 的std::condition_variable
。截至今天(2011),已在 POSIX 接口系统(如 UNIX / LINUX)上完全实现,但仅在 Windows 上进行模仿(部分支持)- Windows CONDITION_VARAIBLE 是实现 std::condition_varable 所需的“硬件” code> 在 Windows 中得到完全支持。但仅从 win6(Vista 和 2008)开始存在。世界上仍然有很多 XP(这就是为什么 mingw -pthread 在 Windows 中不受支持)。
- Winapi evvent 对象(请参阅 CreateEvent)可以模仿 posix 条件变量,但并不完全相同(在 POSIX 中,条件变量可用于根据具体情况弱化一个或全部。在 Windows 中,事件被创建为自动(如果有信号唤醒一个线程并自动重置)或手动(如果有信号弱,则一切都会等待它,直到它保持有信号状态。重置必须是手动的)并且不能根据情况改变行为:
- 忘记了信号和插槽。目的不是“当发生某些事情时弱化等待线程”。它们只是一个完全不同(不相关)的概念,
- 它是更通用的互斥体的“单进程”版本(对于 Windows 和 POSIX 来说是相同的)。区别在于 Windows 不区分递归和非递归:所有都是递归的)
也就是说,在事件(和条件变量)和互斥体(和关键部分)后面有不同的工作范围:
互斥体。 -本质上-保护不能同时运行的代码。它们本质上意味着“如果这段代码已经在执行,请等待另一段代码完成它”。
事件本质上是保护对尚未生成的资源的访问。它本质上意味着“在这里等待,直到有人承认可以过去”。
不同之处在于,互斥体是由发出信号的人发出信号的,而事件是由发出信号的人唤醒的人发出信号的。
请注意,这里的术语“信号”与“增强信号”中的信号概念无关,它与“委托链”的概念更相关。但这完全是另一个故事了。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
如果您想“向所有正在运行的线程发出应用程序将退出的信号”,则手动重置甚至可以解决问题。
您需要让每个线程偶尔检查事件,如果事件已发出信号则退出。
然后,您只需发出事件信号并等待线程完成(如果需要)。
如果您需要多次执行此操作,则情况会更复杂,因为必须手动重置手动重置事件,并且您需要更多代码来确保在重置事件以在后续通知中使用之前每个线程都已收到通知,但是鉴于你的问题,这会很好地工作。
Given you want to "signal all running threads that the application will exit" a manual reset even would do the trick.
You need to have each thread check the event occasionally and exit if the event has been signalled.
You then simply signal the event and wait for the threads to complete if you need to.
It's more complex if you need to do this more than once as a manual reset event must be reset manually and you'd need more code to make sure that every thread has received the notification before you reset the event for use in subsequent notifications, but given your question, this will work just fine.