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:
- Open Device
- WaitOne()
- DeviceOpenedEvent occurs
- Set()
This is what I'm seeing my logs sometimes:
- Open Device
- DeviceOpenedEvent occurs
- 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");
精彩评论