Noi*_*art 1 winapi multithreading
我无法理解PulseEvent或竞争条件。但为了避免它,我试图SetEvent改为,并且ResetEvent每次之前WaitForMultipleObjectsEx.
这是我的流程:
CreateEvent创建自动重置事件,然后我生成并告诉线程二。ResetEvent事件,然后立即开始WaitForMultipleObjectsEx事件和其他一些用于文件监视的内容。如果WaitForMultipleObjectsEx返回,并且不是由于事件,则立即重新启动循环。如果WaitForMultipleObjectsEx返回,由于事件将发出信号,则不要重新启动循环。所以现在请想象一下这种情况:
SetEvent,然后 (2) 向线程 2 发送另一条消息以添加路径,然后 (3) 向线程 2 发送消息以重新启动循环。
SetEvent. 线程 2 将看到它因事件而停止,因此它不会重新启动循环。所以它现在会收到添加路径的消息,所以它会添加路径,然后重新启动循环。SetEvent然后(2)等待消息线程 2,当它收到该消息时,它将终止线程。这会避免竞争条件吗?
谢谢
假设循环需要连续中断两次。您正在想象在线程 ONE 和线程 2 上发生的一系列事件,如下所示:
但是由于您无法控制两个线程之间的时间,因此可能会像这样发生:
即使消息传递机制是同步的,因此在两个读取消息之前 ONE 不会继续,它也可能以这种方式发生:
(显然,如果您使用 PulseEvent,可能会发生类似的事情。)
一个快速的解决方案是在适当的点使用第二个事件为 TWO 发送 ONE 信号,即在重置主事件之后但在等待它之前,但这似乎有些不雅,也不能很好地概括。如果你能保证在足够接近的连续中永远不会有两次中断,你可以简单地选择忽略竞争条件,但请注意,很难对此进行推理,因为理论上它可能需要多长时间没有限制线程二在被换出后恢复运行。
各种替代方案取决于消息在线程和任何其他约束之间传递的方式。 [如果您可以提供有关当前实施的更多信息,我将相应地更新我的答案。]
这是一些更明显的选项的概述。
如果消息传递机制是同步的(如果线程 ONE 在继续之前等待线程 2 接收消息),那么使用单个自动重置事件应该可以正常工作。线程 1 直到线程 2 收到重启循环消息后才会设置事件。如果该事件在线程 TWO 开始等待时已经设置,则仅意味着立即连续发生了两次中断;两个永远不会拖延等待未到来的消息。 [这个潜在的停顿是我能想到的你可能不想使用自动重置事件的唯一原因。如果您有其他问题,请编辑您的问题以提供更多详细信息。]
如果可以非阻塞地发送消息,并且您还没有锁定特定的解决方案,那么这些选项中的任何一个都可能是明智的:
用户模式 APC(QueueUserAPC 函数)提供了一种消息传递机制,可以自动中断可警告的等待。
您可以实现一个简单的队列(受临界区保护),它使用事件来指示是否有待处理的消息。在这种情况下,您可以安全地使用手动重置事件,前提是您仅在拥有保护队列的同一临界区时才对其进行操作。
您可以将自动重置事件与任何类型的线程安全队列结合使用,前提是该队列允许您在不阻塞的情况下测试是否为空。这里的想法是线程 ONE 总是在设置事件之前将消息插入队列,如果线程 2 看到事件已设置但结果队列为空,则忽略该事件。如果考虑效率,您甚至可以找到合适的无锁队列实现。(我不建议您自己尝试。)
(所有这些机制也可以通过使用第二个事件对象来同步。)
我不会推荐以下方法,但如果您碰巧已经在使用其中一种方法进行消息传递,那么您可以通过以下方式使其工作:
如果您使用命名管道进行消息传递,则可以在线程 2 中使用异步 I/O。线程二将在内部使用自动重置事件,您在发出 I/O 调用时指定事件句柄,Windows 在 I/O 到达时设置它。从线程 ONE 的角度来看,只有一个操作。从线程二的角度来看,如果设置了事件,肯定有消息可用。(我相信这与您原来的方法有些相似,您只需要提前而不是事后发出 I/O 调用。)
如果您使用窗口队列进行消息传递,MsgWaitForMultipleObjectsEx() 函数允许您同时等待窗口消息和其他事件。
PS:
文档中提到的 PulseEvent的另一个问题是,这可能会发生:
(我个人对内核不处理这种情况感到有点失望;我原以为它有可能设置一个标志,表示不应恢复等待。但我只能假设这是不切实际的,这是有充分理由的。)