我注意到如果事件发生得太快,我的代码有时会变得不同步。我想知道是否有更好的方法。在正常情况下,我在TestDevice方法中将线程告诉WaitOne后会触发DeviceOpenedEvent,但在某些情况下,我发现事件在线程有机会等待之前被触发。
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();
}
在正常情况下,它看起来像这样:
这就是我有时会看到我的日志:
答案 0 :(得分:2)
由于OpenDevice
是异步的(正如您在注释中所提到的),因此它在与调用者不同的线程中运行。有时它会在源代码执行的下一行之前完成:
OpenDevice(); // Async: may finish before the next line executes!
WaitForEvent = EventEnum.DeviceOpened;
当发生这种情况时DeviceOpenedEvent
无法满足您的要求,因为WaitForEvent
仍然是EventEnum.None
:
if (WaitForEvent == EventEnum.DeviceOpened)
TestAutoResetEvent.Set();
解决方案是更改代码,以便在保证以正确顺序运行的方法内发出完成信号。这是一个简单的实现,它删除枚举并为您需要等待的每个事件使用一个等待句柄:
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();
}
如果您控制OpenDevice
,则更容易:只需在完成后调用deviceOpened.Set()
即可。您甚至可以更改OpenDevice
以接受自动重置事件并在TestDevice
内构建它,这将减少您对多线程错误的曝光。
答案 1 :(得分:1)
这应该不是问题。 AutoResetEvent
状态的documentation:
如果一个线程在调用WaitOne时 AutoResetEvent处于信号状态 状态,线程不会阻塞。
以下代码不会导致WaitOne
阻止,例如:
AutoResetEvent waitHandle = new AutoResetEvent(false);
waitHandle.Set();
waitHandle.WaitOne();
Console.WriteLine("After WaitOne");