简体   繁体   English

ManualResetEvent-此处的竞争情况如何发生?

[英]ManualResetEvent - how might a race condition occur here?

I am trying to determine whether I can use a ManualResetEvent here to ensure that in a concurrent environment, the inner actions of myMethod() are never invoked concurrently. 我试图确定是否可以在此处使用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 - Note I am using ManualResetEvent instead of just lock() because I want to be able to add a timeout, potentially. 编辑 -注意,我使用的是ManualResetEvent而不是lock(),因为我希望能够添加超时。

You have at least one opportunity for a race condition. 您至少有一个竞赛条件的机会。 Consider: 考虑:

Thread #1 executes the _syncEvent.WaitOne() and succeeds, then gets swapped out before it can execute the _syncEvent.Close() . 线程#1执行_syncEvent.WaitOne()并成功执行,然后在执行_syncEvent.Close()之前被换出。 Thread #2 comes along and executes the WaitOne() , and also succeeds. 线程#2出现并执行WaitOne() ,并且也成功。

Another problem is that you're calling Close() followed by constructing a new instance, apparently as a way to reset. 另一个问题是您要先调用Close()然后构造一个新实例,显然是一种重置方法。 Imagine, then, that you call Close() , the thread is swapped out, the next thread comes along and tries to do WaitOne() , and throws an exception because the object has been closed. 想象一下,然后,您调用Close() ,线程被换出,下一个线程出现并尝试执行WaitOne() ,并由于对象已关闭而引发异常。

If you want to reset the event, call Reset() . 如果要重置事件,请调用Reset()

You probably can't make this work with a ManualResetEvent . 您可能无法通过ManualResetEvent进行此操作。 As others have said, ManualResetEvent is used for signaling, not mutual exclusion. 正如其他人所说, ManualResetEvent用于发信号,而不是互斥。

You say that you'll want to implement a timeout in the future. 您说您将来想实现超时。 If you just want a thread to wait on the lock for a period of time and then exit if it can't obtain the lock, use one of the Monitor.TryEnter overloads that accept a timeout value. 如果只希望线程在锁上等待一段时间,然后在无法获取锁的情况下退出,请使用Monitor.TryEnter重载之一,该重载接受超时值。 For example: 例如:

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
    }
}

There is some debate over whether you really want to release the lock in the finally . 有超过您是否真的要解除锁定在一些争论finally If the code throws an exception, then it's possible (likely?) that the resource you were protecting is now in an incomplete or otherwise corrupt state. 如果代码抛出异常,则可能(可能是?)您正在保护的资源现在处于不完整或损坏的状态。 In that case, you might not want to let other threads act on it. 在这种情况下,您可能不想让其他线程对其执行操作。 Whether you release the lock in the face of exceptions is a design decision that you'll have to make, consistent with your application's requirements. 是否面对异常释放锁是必须根据应用程序要求做出的设计决定。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM