Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Synchronizing Events

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 forever
like image 883
Robert Avatar asked Feb 13 '26 05:02

Robert


1 Answers

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.

like image 189
Jeff Sternal Avatar answered Feb 14 '26 18:02

Jeff Sternal



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!