想象一下,我有一个类,其方法可由多个线程访问。想象一下,该类包含一个整数“i”作为私有字段,并且某些方法增加/减少该值。
最后想象一下,每当我= = 5时,我的一个方法都需要阻塞(使用AutoResetEvent)
我会写:
if(i == 5)
myAutoResetEvent.WaitOne()
但是,如果在我检查“i”的值和我调用WaitOne的那一刻之间另一个线程改变了“i”,那该怎么办?
我无法使用lock块包装代码,因为如果未发出myAutoResetEvent信号,它将永远保持阻塞状态。
任何解决方案? 感谢
答案 0 :(得分:9)
您指定的行为类似于CountdownEvent,但CountdownEvent
不允许该计数器为负数(因此CountdownEvent已初始化为5韩元&t; t解决问题...)
Joe Albahari实施了CountdownEvent template,可以通过一些更改来解决您的问题:
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);
}
}
由于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();
除非_i
等于5 ...
答案 1 :(得分:0)
如果我理解正确,你的i应该是易变的,这样每次线程访问它时,它都会获得最新的可用值。
然而,谁更新了我?我建议查看Interlocked.Increment / Decrement方法,以便确保我以线程安全的方式递增。
答案 2 :(得分:0)
您可以使用double check lock strategy。虽然你提到你不想使用锁,因为它将永远被阻止,这里使用双重检查,你确保锁只在它5时执行一次,其余的时间它不会阻塞。
当i == 5
时,一个线程将进入WaitOne传递双重检查区域,该线程的其余部分将被锁定阻止,并且无法进入WaitOne。
当您发出Set()
信号时,等待线程将从WaitOne中释放,然后它将释放锁定的块并继续。等待锁定的其他线程之一现在将进入双重检查,现在如果我不再== 5,则不会进入WaitOne。
其他线程将遵循相同的模式,如果再次i == 5
,其中一个将再次进入WaitOne。
private static lock locker = new object();
public MyMethod()
{
if (i==5)
{
lock(locker)
{
if (i==5) // is it really == or should it be >=
{
resetEvent.WaitOne();
}
}
}
//i was not 5, do whatever other work that needs to be done.
//at some point you would want to decrement i and signal
//either here or some other method that is finishing the task
//you can use double check here on a separate lock variable
//As you haven't described who increments/decrements i, its not clear
//where/how you are managing i increment/decrement.
Interlocked.Decrement(ref i);
if (i < 5)
{
resetEvent.Set();
}
}
答案 3 :(得分:0)
我看到的最佳解决方案是使用Monitor和Wait
/ {{3将AutoResetEvent
替换为基于自定义Pulse
的同步/信令构造像这样的方法:
班级成员:
private object syncLock = new object();
private int i = 0;
增量/减量方法:
private void Increment()
{
lock (syncLock)
{
i++;
Monitor.PulseAll(syncLock);
}
}
private void Decrement()
{
lock (syncLock)
{
i--;
Monitor.PulseAll(syncLock);
}
}
示例等待(问题):
lock (syncLock)
{
while (i == 5)
Monitor.Wait(syncLock);
}
请注意,由于文档中描述的唯一Wait
方法行为,上述工作原理如下:
释放对象的锁定并阻止当前线程,直到它重新获取锁定。
然后在备注部分:
当前拥有指定对象上的锁的线程调用此方法以释放该对象,以便另一个线程可以访问它。等待重新获取锁定时,调用者被阻止。当调用者需要等待由于另一个线程的操作而发生的状态更改时,将调用此方法。