本文介绍了SetEvent ResetEvent WaitForMultipleObjectsEx-竞赛条件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法理解PulseEvent或比赛状况.但是为了避免这种情况,我尝试改为SetEvent,并且每次ResetEventWaitForMultipleObjectsEx之前.

I am not able to understand the PulseEvent or race condition. But to avoid it I am trying to SetEvent instead, and ResetEvent every time before WaitForMultipleObjectsEx.

这是我的流程:

  • 线程一-使用CreateEvent创建一个自动重置事件,然后生成并告诉线程2.
  • 线程一-告诉线程二运行.
    • 线程2将在事件上执行ResetEvent,然后立即在事件上启动WaitForMultipleObjectsEx和其他一些文件监视功能.如果WaitForMultipleObjectsEx返回,并且不是由于该事件引起的,则立即重新启动循环.如果WaitForMultipleObjectsEx返回,由于要通知事件,则不要重新启动循环.
    • Thread One - Uses CreateEvent to create an auto reseting event, I then spawn and tell Thread TWO about it.
    • Thread One - Tell thread TWO to run.
      • Thread TWO will do ResetEvent on event and then immediately start WaitForMultipleObjectsEx on the event and some other stuff for file watching. If WaitForMultipleObjectsEx returns, and it is not due to the event, then restart the loop immediately. If WaitForMultipleObjectsEx returns, due to event going to signaled, then do not restart loop.

      现在,请想象这种情况:

      So now imagine this case please:

      • 线程2-循环正在运行
      • 线程一-需要添加路径,所以它这样做(1)SetEvent,然后(2)向线程2发送另一条消息以添加路径,然后(3)向线程2发送消息以重新启动环形.
        • 添加路径和重新启动循环的消息将不会进入线程TWO,除非我在两个线程中停止了循环,这是由SetEvent完成的.线程2将看到由于事件该线程已停止,因此它不会重新启动循环.因此,它现在将获得消息以添加路径,因此它将添加路径,然后重新启动循环.
        • Thread TWO - loop is running
        • Thread One - needs to add a path, so it does (1) SetEvent, and then (2) sends another message to thread 2 to add a path, and then (3) sends message to thread 2 to restart loop.
          • The messages of add path and restart loop will not come in to Thread TWO unless I stop the loop in TWO, which is done by the SetEvent. Thread TWO will see it was stoped due to the event, and so it wont restart the loop. So it will now get the message to add path, so it will add path, then restart loop.

          这会避免比赛条件吗?

          谢谢

          推荐答案

          假定循环需要连续两次中断.您正在想象在线程ONE和线程2上发生一系列类似的事件:

          Suppose the loop needs to be interrupted twice in succession. You're imagining a sequence of events something like this, on thread ONE and thread TWO:

          1. 线程ONE意识到第一个中断已完成.
          2. 线程ONE发送一条消息,告诉两次重新启动等待循环.
          3. 线程2读取消息重新启动等待循环".
          4. 线程2重置事件.
          5. 线程2开始等待.
          6. 线程一现在意识到需要另一个中断.
          7. 线程一将事件设置为请求另一个中断.
          8. 线程ONE发送与第二次中断有关的消息.
          9. 线程2停止循环,接收有关第二次中断的消息.

          但是由于您无法控制两个线程之间的时间安排,因此可能会发生以下情况:

          But since you don't have any control over the timing between the two threads, it might instead happen like this:

          1. 线程ONE意识到第一个中断已完成.
          2. 线程ONE发送一条消息,告诉两次重新启动等待循环.
          3. 线程一现在意识到需要另一个中断.
          4. 线程一将事件设置为请求另一个中断.
          5. 线程2读取消息重新启动等待循环".
          6. 线程2重置事件.
          7. 线程2开始等待.
          8. 线程ONE发送有关第二次中断的消息,但是两个没有监听!

          即使消息传递机制是同步的,所以直到两个读取消息后,消息传递机制才会继续,

          Even if the message passing mechanism is synchronous, so that ONE won't continue until TWO has read the message, it could happen this way:

          1. 线程ONE意识到第一个中断已完成.
          2. 线程ONE发送一条消息,告诉两次重新启动等待循环.
          3. 线程2读取消息重新启动等待循环",但随后被换出.
          4. 线程一现在意识到需要另一个中断.
          5. 线程一将事件设置为请求另一个中断.
          6. 线程2重置事件.
          7. 线程2开始等待.
          8. 线程ONE发送有关第二次中断的消息,但是两个没有监听!

          (很明显,如果使用PulseEvent,可能会发生类似的事情.)

          (Obviously, a similar thing can happen if you use PulseEvent.)

          一种快速的解决方案是对第二个事件使用第二个事件在适当的点发出信号,即在重置主事件之后但在等待它之前,但这似乎有些微不足道,并且也不能很好地概括.如果可以保证在足够的连续时间内不会出现两次中断,则可以选择忽略竞争条件,但是请注意,由于存在任何理论上的限制,因此很难对此进行推理.线程2被换出后恢复运行.

          One quick solution would be to use a second event for TWO to signal ONE at the appropriate point, i.e., after resetting the main event but before waiting on it, but that seems somewhat inelegant and also doesn't generalize very well. If you can guarantee that there will never be two interruptions in close-enough succession, you might simply choose to ignore the race condition, but note that it is difficult to reason about this because there is no theoretical limit to how long it might take for thread TWO to resume running after being swapped out.

          各种选择取决于消息如何在线程之间传递以及任何其他约束. [如果您可以提供有关当前实施方式的更多信息,我将相应地更新我的答案.]

          The various alternatives depend on how the messages are being passed between the threads and any other constraints. [If you can provide more information about your current implementation I'll update my answer accordingly.]

          这是一些较明显的选项的概述.

          This is an overview of some of the more obvious options.

          如果消息传递机制是同步的(如果线程ONE等待线程2等待线程继续接收消息),则使用单个自动重置事件应该可以正常工作.直到线程2收到重新启动循环消息后,线程ONE才设置事件.如果线程TWO开始等待时已经设置了事件,则意味着立即继承中发生了两次中断.两个永远不会停止等待不来的消息. [此潜在的停顿是我想到为什么您可能不想使用自动重置事件的唯一原因.如果您还有其他问题,请编辑问题以提供更多详细信息.]

          If the message-passing mechanism is synchronous (if thread ONE waits for thread TWO to receive the message before proceeding) then using a single auto-reset event should just work. Thread ONE won't set the event until after thread TWO has received the restart-loop message. If the event is already set when thread TWO starts waiting, that just means that there were two interruptions in immediate succession; TWO will never stall waiting for a message that isn't coming. [This potential stall is the only reason I can think of why you might not want to use an auto-reset event. If you have another concern, please edit your question to provide more details.]

          如果可以发送不阻塞的消息是可以的,并且您还没有锁定特定的解决方案,那么以下任何一个选项都可能是明智的:

          If is OK for sending a message to be non-blocking, and you aren't already locked in to a particular solution, any of these options would probably be sensible:

          • 用户模式APC(QueueUserAPC功能)提供了一种消息传递机制,该机制可自动中断可警报的等待.

          • User mode APCs (the QueueUserAPC function) provide a message-passing mechanism that automatically interrupts alertable waits.

          您可以实现一个简单的队列(由关键部分保护),该队列使用事件指示是否有待处理的消息.在这种情况下,您可以安全地使用手动重置事件,前提是您只有在拥有保护队列的相同关键部分时才对其进行操作.

          You could implement a simple queue (protected by a critical section) which uses an event to indicate whether there is a message pending or not. In this case you can safely use a manual-reset event provided that you only manipulate it when you hold the same critical section that protects the queue.

          您可以将自动重置事件与任何类型的线程安全队列结合使用,前提是该队列允许您测试是否为空而不阻塞.这里的想法是线程ONE总是在设置事件之前 将消息插入队列中,如果线程TWO看到设置了事件,但事实证明队列为空,则事件为忽略了.如果效率是一个问题,您甚至可以找到合适的无锁队列实现. (我不建议您自己尝试.)

          You could use an auto-reset event in combination with any sort of thread-safe queue, provided only that the queue allows you to test for emptiness without blocking. The idea here is that thread ONE would always insert the message into the queue before setting the event, and if thread TWO sees that the event is set but it turns out that the queue is empty, the event is ignored. If efficiency is a concern, you might even be able to find a suitable lock-free queue implementation. (I don't recommend attempting that yourself.)

          (所有这些机制也可以通过使用第二个事件对象来同步.)

          (All of those mechanisms could also be made synchronous by using a second event object.)

          我不建议您使用以下方法,但是如果您碰巧已经在使用其中一种进行消息传递,则可以采用以下方法:

          I wouldn't recommend the following approaches, but if you happen to already be using one of these for messaging this is how you can make it work:

          • 如果使用命名管道进行消息传递,则可以在线程TWO中使用异步I/O.线程2将在内部使用自动重置事件,在发出I/O调用时指定事件句柄,而Windows在I/O到达时设置事件句柄.从线程ONE的角度来看,只有一个操作.从线程2的角度来看,如果设置了事件,则肯定有一条消息. (我相信这有点像您的原始方法,您只需要提前发出I/O调用即可,而不是之后.)

          • If you're using named pipes for messaging, you could use asynchronous I/O in thread TWO. Thread TWO would use an auto-reset event internally, you specify the event handle when you issue the I/O call and Windows sets it when I/O arrives. From the point of view of thread ONE, there's only a single operation. From the point of view of thread TWO, if the event is set, a message is definitely available. (I believe this is somewhat similar to your original approach, you just have to issue the I/O call in advance rather than afterwards.)

          如果您使用窗口队列进行消息传递,则MsgWaitForMultipleObjectsEx()函数可让您同时等待窗口消息和其他事件.

          If you're using a window queue for messaging, the MsgWaitForMultipleObjectsEx() function allows you to wait for a window message and other events simultaneously.

          PS:

          PulseEvent的 other 问题是该文档中提到的问题,

          The other problem with PulseEvent, the one mentioned in the documentation, is that this can happen:

          1. 线程2开始等待.
          2. 线程2被Windows抢占,并且线程上的所有用户代码停止运行.
          3. 线程一触发事件.
          4. 线程2由Windows重新启动,并重新开始等待.
          5. 线程ONE发送一条消息,但是两个没有在监听.

          (我个人对内核无法处理这种情况感到有些失望;我本以为它可能会设置一个标志,表示不应恢复等待.但是我可以仅假设有充分的理由说明这是不切实际的.)

          (Personally I'm a bit disappointed that the kernel doesn't deal with this situation; I would have thought that it would be possible for it to set a flag saying that the wait shouldn't be resumed. But I can only assume that there is a good reason why this is impractical.)

          这篇关于SetEvent ResetEvent WaitForMultipleObjectsEx-竞赛条件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-22 17:21