开发者

Synchronizing Events

开发者 https://www.devze.com 2023-03-12 06:44 出处:网络
I noticed that sometimes my code becomes out of sync if an event fires too quickly.I was wondering if there was a better approach. Under a normal scenario the DeviceOpenedEvent fires after I tell the

I noticed that sometimes my code becomes out of sync if an event fires too quickly. I was wondering if there was a better approach. Under a normal scenario the DeviceOpenedEvent fires after I tell the thread to WaitOne in the TestDevice method, but I have seen in some cases where the event gets fired before the thread has a chance to wait.

    protected AutoResetEvent TestAutoResetEvent = new AutoResetEvent(false);
    public EventEnum WaitForEvent = EventEnum.None;

    bool TestDevice()
    {
        OpenDevice();

        WaitForEvent = EventEnum.DeviceOpened;
        TestAutoResetEvent.WaitOne();
        WaitForEvent = EventEnum.NoWait;

        //Continue with other tests
    }

    void DeviceOpenedEvent()
    {
        if (WaitForEvent == EventEnum.DeviceOpened)         
            TestAutoResetEvent.Set();                           
    }

Under normal circumstances it looks like this:

  1. Open Device
  2. WaitOne()
  3. DeviceOpenedEvent occurs
  4. Set()

This is what I'm seeing my logs sometimes:

  1. Open Device
  2. DeviceOpenedEvent occurs
  3. WaitOne() Essentially stuck here forev开发者_如何转开发er


Since OpenDevice is asynchronous (as you mentioned in a comment), it runs in a different thread than its caller. Sometimes it will finish before the next line in source executes:

    OpenDevice(); // Async: may finish before the next line executes!
    WaitForEvent = EventEnum.DeviceOpened;

When that happens DeviceOpenedEvent doesn't do what you want it to, because WaitForEvent is still EventEnum.None:

if (WaitForEvent == EventEnum.DeviceOpened)         
    TestAutoResetEvent.Set(); 

The solution is to change your code so that you signal completion inside a method that's guaranteed to run in the correct order. Here's a simple implementation that removes the enumeration and uses a single wait handle for each event you need to wait on:

protected AutoResetEvent deviceOpenedEvent = new AutoResetEvent(false);
protected AutoResetEvent deviceLockedEvent = new AutoResetEvent(false);

bool TestDevice() {
    OpenDevice();
    // Do some unrelated parallel stuff here ... then
    deviceOpenedEvent.WaitOne();
    LockDevice();
    deviceLockedEvent.WaitOne();
}

void DeviceOpenedEvent() {
    deviceOpenedEvent.Set();                           
}

It's even easier if you control OpenDevice: just call deviceOpened.Set() when it's done. You could even change OpenDevice to accept the auto reset event and construct it right inside TestDevice, which would reduce your exposure to multithreading bugs.


This should not be a problem. The documentation for AutoResetEvent states:

If a thread calls WaitOne while the AutoResetEvent is in the signaled state, the thread does not block.

The following code does not cause WaitOne to block, for instance:

AutoResetEvent waitHandle = new AutoResetEvent(false);
waitHandle.Set();
waitHandle.WaitOne();
Console.WriteLine("After WaitOne");
0

精彩评论

暂无评论...
验证码 换一张
取 消