AutoResetEvent类型是原子开关的合适选择吗?

时间:2010-09-09 06:37:10

标签: .net multithreading boolean atomic autoresetevent

假设我正在处理来自多个线程的大量传入数据。我可能希望这些数据在满足某些条件时充当特定操作的触发器。但是,该行动不是可重入的;因此,快速连续触发两次相同的触发器只会导致操作运行一次。

使用简单的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); } } 。这看起来很简单。

2 个答案:

答案 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监视器“长大”,所以它更接近我的背景。)