如何将事件发送到多个线程

时间:2011-10-20 06:05:26

标签: c++ windows boost

如何将事件发送到应用程序的多个正在运行的线程?

例如:我的主线程想要通知应用程序将退出的所有正在运行的线程。

我有点困惑哪些可能性导致了一个简单可靠的解决方案:

    来自condition_variablenotify_all
  • boost 来自WinApi的CONDITION_VARIABLE
  • WakeAllConditionsVariable
  • 来自WinApi的
  • CEvent
  • 在主题上使用mutextry_lock
  • 来自signal
  • slotsboost
  • 甚至是一个CriticalSection的全局变量,用于保护getter / setter
  • 在此处插入您的解决方案 ...

你能否指出一个初学者正确的方向,也许还有一些关于我提供的可能性的话。

补充: - 操作系统是Windows XP

4 个答案:

答案 0 :(得分:2)

鉴于您希望“发出应用程序将退出的所有正在运行的线程的信号”,手动重置甚至可以解决问题。

您需要让每个线程偶尔检查一次事件,并在事件已发出信号后退出。

然后,您只需发出信号,并在需要时等待线程完成。

如果您需要多次执行此操作会更复杂,因为手动重置事件必须手动重置,并且您需要更多代码以确保每个线程都已收到通知,然后重置事件以便在后续使用通知,但鉴于你的问题,这将工作得很好。

答案 1 :(得分:2)

这里有两个不同的问题。第一个是如何使一些数据可用于代码的不同部分,以便不同的线程可以读取它,而第二个是如何以线程安全的方式修改一些状态,以便线程可以采取行动。

要为不同的线程提供一些数据,您可以使用不同的方法:

  • 如果您有对管理线程的对象的引用,您可以使用它们手动遍历线程列表并通知每个线程
  • 您可以在程序中的某个位置使用boost :: signal来管理这些引用(即创建exit信号,并在构造时连接所有线程。
  • 如果您没有这些引用,可以将exit标志设置为全局变量并从中读取线程

我倾向于更喜欢每个线程由单个对象管理,在这种情况下,我会尝试将数据位移动到该管理器并避免全局。在大多数情况下,设计会建立资源的依赖关系和所有权,在这种情况下,您不需要信号机制,因为您可以处理这些依赖关系。

问题的第二部分是如何以线程安全的方式将数据从主线程传递给其余线程,并且在这种特殊情况下,在大多数情况下具有一个值的单个数据程序并在一个点设置为不同的固定值。这是一个全局变量还是线程管理器状态的一部分并不重要:

  • 的互斥。每个线程在读/写之前锁定,确保修改是安全的
  • 原子操作。对于足够小的数据类型,线程库/ OS提供原子操作,这些操作将添加适当的内存屏障以确保数据移动

结合你所拥有的是全局或局部变量,它将直接(全局)或通过主线程触发的函数调用直接或通过某些信号机制进行修改。标志的修改必须通过原子操作(引入内存栅栏)或通过持有互斥锁来完成。

如果线程可以处于非活动状态(等待条件变量),那么你可能想要避免全局,调用函数,锁定互斥锁和互斥锁内部都改变标志并使用条件变量来唤醒等待线程。等待线程将负责在条件等待时返回时检查标志。

答案 2 :(得分:2)

如果您只是测试状态的简单更改(何时退出),则条件变量就足够了。 请确保您找到一个好的WinAPI示例:您几乎肯定需要多次测试状态(与您的直觉可能告诉您的不同)。

如果您打算在线程之间共享任务/事件,那么一个好的模式是使用基于互斥队列和条件变量的生产者 - 消费者模式。一个典型的例子是,你有一个生产者线程,它产生被推入队列的作业(例如,每个任务将数据写入不同的文件)。然后,您可以拥有多个使用者线程,这将从队列中删除任务并对其执行操作。要在线程之间进行通信,您可以使用条件变量,它将向使用者线程发出信号,表示互斥队列可能为空。 (阅读条件变量以理解为什么我在这里说“可能”)。

当您进行越来越多的线程编程时,您将寻找不需要互斥锁的机会。例如,如果我在磁盘上有大量只读数据,并且我希望您的线程计算这些数据,则可以在生成线程之前读取数据 。然后,您的所有线程都可以访问此数据,而无需使用锁,只要它们的行为和不写入数据即可。您会发现只使用互斥锁和条件变量实现了许多线程模式(部分原因是POSIX线程库非常小)。

最后一个注意事项:它有点超出了你的问题的范围,但你也会感觉到你真正需要产生多少线程。线程之间的上下文切换相对便宜,但成本肯定不为零。这意味着在产生更多线程时,有一个收益递减点。根据操作系统考虑一下:除了实际进行切换之外,算法还必须花费周期来决定何时进行上下文切换。这个成本不是零。因此,当您进行更多线程编程时,您会发现另一种类型的优化,即在线程中执行尽可能多的工作而无需等待另一个线程来完成工作。当然,您必须在设计的清洁度和优化速度之间取得平衡。在设计应用程序时,这只是需要考虑的事情。

答案 3 :(得分:1)

  1. boost::condition_variable期待C ++ 11的std::condition_variable。截至今日(2011年)完全在POSIX接口系统(如UNIX / LINUX)上实现,但仅在Windows上模仿(部分支持)
  2. windows CONDITION_VARAIBLE是需要在完全支持的Windows中实现std::condition_varable的“硬件”。但仅存在于win6(Vista和2008)。这个世界仍然受到很多XP的影响(这就是为什么windows中不支持mingw -pthread)。
  3. Winapi evvent对象(请参阅CreateEvent)可以模仿posix条件变量,但不完全相同(在POSIX中,条件变量可以用于弱化一个或全部,逐个案例。在Windows中,创建一个事件是自动的(如果发出信号唤醒一个线程和自动复位)或手动(如果发出信号弱点,一切都等待它直到它将保持信号状态。重置必须是手动的)并且不能逐个改变行为。
  4. 忘记信号&插槽:他们的目的不是“在发生某些事情时弱化等待线程”。它们只是一个完全不同(不相关)的概念。
  5. CriticalSection是更通用的Mutex的“单进程”版本(对于Windows和POSIX来说是相同的,区别在于Windows不区分递归和非递归:所有都是递归的。)
  6. 也就是说,在事件(和条件变量)和互斥(以及关键部分)之后有不同的工作范围:

    Mutexes - 保护 - 不能同时运行的代码。它们实质上意味着“如果这段代码已经执行,那么等到另一个完成它”。

    事件 - 基本上 - 保护对尚未生成的资源的访问。它本质上意味着“在这里等到有人可以批准它可以过去”。

    不同之处在于,互斥量由同样没有信号的人发出信号,而事件则是由发出信号的人发出的信号所取消。 注意,这里的术语“信号”与“增强信号”中的信号概念有关,这与“代表链”的概念更为相关。但这是另一回事。