4

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

使用简单的bool标志来指示操作当前是否正在运行并不是一个可靠的解决方案,因为可能会发生竞争条件,并且两个(或更多)线程最终仍然可能最终同时运行相同的操作。当然,在同步对象上的语句内bool是可行的;lock但我通常更愿意尽可能避免锁定*。

目前我在这些情况下所做的AutoResetEvent基本上是使用原子开关的一种形式。代码通常如下所示:

if (conditionMet && readyForAction.WaitOne(0))
{
    try
    {
        doAction();
    }
    finally
    {
        readyForAction.Set();
    }
}

这行得通,但令我震惊的是,这可能是该类的不太理想的用例AutoResetEvent。您会说这是解决此问题的适当方法,还是使用其他机制更有意义?


更新:正如乔恩指出的那样,使用Monitor.TryEnter作为锁定策略(而不是简单的lock,我认为它大致相当于Monitor.Enter),真的非常简单:

if (conditionMet && Monitor.TryEnter(lockObject))
{
    try
    {
        doAction();
    }
    finally
    {
        Monitor.Exit(lockObject);
    }
}

也就是说,我强烈倾向于支持Henk使用Interlocked. 这似乎很简单。

4

2 回答 2

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
    }
}
于 2010-09-09T06:41:32.807 回答
2

“我通常更喜欢尽可能避免锁定” - 为什么?看起来您在这里有效地做着非常相似的事情,但使用的是 Windows 同步原语而不是 CLR 同步原语。您正在尝试同步,以便一次只有一个线程可以运行一段特定的代码……这对我来说听起来确实像是一把锁。唯一的区别是如果锁已经被持有,你根本不想费心执行代码。

您应该能够使用 实现相同的效果Monitor.TryEnter,我个人认为这更容易理解。(再说一次,我是用 Java 监视器“长大”的,所以它更接近我的背景。)

于 2010-09-09T06:46:55.163 回答