ManualResetEvent -竞态条件是如何在这里发生的
本文关键字:在这里 条件 ManualResetEvent | 更新日期: 2023-09-27 17:50:05
我想确定我是否可以在这里使用ManualResetEvent
来确保在并发环境中,myMethod()
的内部动作永远不会并发调用。
static volatile bool _syncInitialized = false;
static ManualResetEvent _syncEvent = new ManualResetEvent(false);
static object _syncLock = new object();
void myMethod()
{
lock (_syncLock)
{
if (!_syncInitialized) // sync hasn't started, so
{
_syncInitialized = true;
_syncEvent.Set(); // signal for the first time only
}
}
if (_syncEvent.WaitOne()) // wait for signal
{
_syncEvent.Close();
_syncEvent = new ManualResetEvent(false); // reset to false
// actions that should be forced to run sequentially
}
}
EDIT -注意我使用ManualResetEvent而不是lock(),因为我希望能够添加超时,潜在的。
至少有一次出现竞争条件的机会。考虑:
线程#1执行_syncEvent.WaitOne()
并成功,然后在执行_syncEvent.Close()
之前被交换。线程#2出现并执行WaitOne()
,也成功了。
另一个问题是,你调用Close()
之后构造一个新的实例,显然是一种重置的方式。那么,想象一下,如果您调用Close()
,线程被换出,下一个线程出现并试图执行WaitOne()
,并抛出异常,因为对象已经关闭。
如果需要重置事件,请调用Reset()
。
您可能无法使用ManualResetEvent
来完成此工作。正如其他人所说,ManualResetEvent
用于信令,而不是互斥。
你说你将来想要实现一个超时。如果您只想让线程在锁上等待一段时间,然后在无法获得锁时退出,那么可以使用Monitor之一。接受超时值的TryEnter重载。例如:
private object _syncObject = new Object();
void MyMethod()
{
if (!Monitor.TryEnter(_syncObject, TimeSpan.FromSeconds(5)))
{
return; // couldn't get the lock
}
try
{
// got the lock. Do stuff here
}
finally
{
Monitor.Exit(); // release the lock
}
}
关于你是否真的想要释放finally
中的锁有一些争论。如果代码抛出异常,那么您正在保护的资源现在可能(很可能?)处于不完整或损坏的状态。在这种情况下,您可能不想让其他线程对它进行操作。是否在遇到异常时释放锁是您必须做出的设计决策,与应用程序的需求保持一致。