代码之家  ›  专栏  ›  技术社区  ›  Dan Tao

autoresetEvent类型是否是原子开关的适当选择?

  •  4
  • Dan Tao  · 技术社区  · 14 年前

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

    使用简单 bool 用于指示操作当前是否正在运行的标志不是一个健壮的解决方案,因为可能会发生争用情况,而且两个(或多个)线程最终仍可能同时运行同一个操作。一 布尔 在一个 lock 当然,同步对象上的语句是可以工作的;但我通常希望尽可能避免锁定*。

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

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

    这是可行的,但我突然想到,对于 自动修复事件 班级。你会说这是解决这个问题的合适方法,还是使用其他机制更有意义?


    更新 作为 Jon pointed out 使用 Monitor.TryEnter 作为锁定策略(而不是简单的 我认为大致相当于 Monitor.Enter )非常简单:

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

    也就是说,我非常愿意 Henk's idea 使用的 Interlocked . 这似乎很简单。

    2 回复  |  直到 14 年前
        1
  •  3
  •   Henk Holterman    14 年前

    简单(快速) 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
        }
    }
    
        2
  •  2
  •   Jon Skeet    14 年前

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

    你应该能够达到同样的效果 Monitor.TryEnter 我个人认为,这更容易理解。(再一次,我用Java监视器“长大”了,所以它离我的背景更近了。)