ManualResetEvent - 如何在这里发生竞争条件?

时间:2012-12-29 20:29:57

标签: c# concurrency thread-safety race-condition manualresetevent

我正在尝试确定是否可以在此使用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
        }
    }

编辑 - 注意我使用的是ManualResetEvent而不是lock(),因为我希望能够添加超时。

1 个答案:

答案 0 :(得分:3)

您至少有一次参加比赛的机会。考虑:

线程#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中释放锁定存在争议。如果代码抛出异常,则可能(可能?)您保护的资源现在处于不完整或其他损坏的状态。在这种情况下,您可能不希望让其他线程对其执行操作。无论您是否在例外情况下解锁,都必须根据应用程序的要求做出设计决策。