检查控制变量后调用AutoResetEvent.WaitOne

Zé *_*los 1 c# concurrency multithreading thread-safety

想象一下,我有一个类,其方法可由多个线程访问.想象一下,该类包含一个整数"i"作为私有字段,并且某些方法增加/减少该值.

最后想象一下,每次i == 5时,我的一个方法都需要阻塞(使用AutoResetEvent)

我会写:

if(i == 5)
  myAutoResetEvent.WaitOne()
Run Code Online (Sandbox Code Playgroud)

但是如果在我检查"i"的值和我调用WaitOne的那一刻之间另一个线程改变了"我"呢?

我无法使用锁定块包装代码,因为如果未发出myAutoResetEvent信号,它将永远保持阻塞状态.

对此有何解决方案?谢谢

Old*_*Fox 9

您指定的行为类似于CountdownEvent,但是CountdownEvent不允许计数器为负数(因此初始化为5的CountdownEvent将无法解决问题...)

Joe Albahari已经实现了一个CountdownEvent模板,可以通过一些更改来解决您的问题:

public class EqualsWaitHandle
{
    private readonly object _locker = new object();

    private readonly int _lockValue;
    public int Value { get; private set; }

    public EqualsWaitHandle(int lockValue = 0, int initialCount = 0)
    {
        Value = initialCount;
        _lockValue = lockValue;
    }

    public void Signal() { AddCount(-1); }

    public void AddCount(int amount)
    {
        lock (_locker)
        {
            Value += amount;
            if (Value != _lockValue)
                Monitor.PulseAll(_locker);
        }
    }

    public void Wait()
    {
        lock (_locker)
            while (Value  == _lockValue)
                Monitor.Wait(_locker);
    }
}
Run Code Online (Sandbox Code Playgroud)

由于i等待请求之间存在关联(并且您不应该违反SRP ...),因此最好将其作为一个对象进行管理......

顺便说一句,如果您不想将此类/合并到您的类中,您可以使用ManualResetEventSlim实现该行为:

public void AddToVal(int num)
{
    lock (_syncObj)
    {
        _i += num;
        if (_i == 5)
        {
            _event.Reset();
            return;
        }

        _event.Set();
    }
}

// and in the waitable thread:
_event.wait();
Run Code Online (Sandbox Code Playgroud)

该事件不会阻止,除非_i等于5 ...