假设我正在处理来自多个线程的大量传入数据。我可能希望这些数据在满足某些条件时充当特定操作的触发器。但是,该行动不是可重入的;因此,快速连续触发两次相同的触发器只会导致操作运行一次。
使用简单的bool
标志来指示操作当前是否正在运行不是一个强大的解决方案,因为竞争条件可能会发生,并且两个(或更多)线程仍然可能最终同时运行相同的操作。当然,同步对象的bool
语句中的lock
可以正常工作;但我通常更愿意尽可能避免锁定*。
目前我在这些情况下所做的是使用AutoResetEvent
作为原子开关的一种形式。代码通常如下所示:
if (conditionMet && readyForAction.WaitOne(0))
{
try
{
doAction();
}
finally
{
readyForAction.Set();
}
}
这很有效,但令我印象深刻的是,对于AutoResetEvent
类来说,这可能是一个不太理想的用例。你是说这是解决这个问题的合适方法,还是使用其他机制更有意义?
更新:作为Jon pointed out,使用Monitor.TryEnter
作为锁定策略(而不是简单的lock
,我相信它大致相当于{{1} ,},真的很简单:
Monitor.Enter
那就是说,我非常倾向于Henk's idea使用if (conditionMet && Monitor.TryEnter(lockObject))
{
try
{
doAction();
}
finally
{
Monitor.Exit(lockObject);
}
}
。这看起来很简单。
答案 0 :(得分:3)
简单(快速)System.Threading.Interlocked.CompareExchange()
即可。
//untested
int flag = 0;
if (conditionMet && Interlocked.CompareExchange(ref flag, 1, 0) == 0)
{
try
{
doAction();
}
finally
{
flag = 0; // no need for locking
}
}
答案 1 :(得分:2)
“我通常更愿意尽可能避免锁定” - 为什么?看起来你有效在这里做了非常相似的事情,但是使用Windows同步原语而不是CLR原语。你正在尝试同步,这样一次只有一个线程可以运行一段特定的代码......这听起来像是对我的锁定。唯一的区别是,如果锁定已经存在,您根本不想打扰执行代码。
你应该能够使用Monitor.TryEnter
达到同样的效果,我个人认为这有点简单易懂。 (然后,我再次使用Java监视器“长大”,所以它更接近我的背景。)