【问题标题】:Is this a correct Interlocked synchronization design?这是一个正确的联锁同步设计吗?
【发布时间】:2010-04-07 21:23:12
【问题描述】:

我有一个采集样本的系统。我在应用程序中有多个对这些样本感兴趣的客户端线程,但获取样本的实际过程只能在一个上下文中发生。它足够快,可以阻止调用进程直到采样完成,但足够慢,我不希望多个线程堆积请求。我想出了这个设计(精简到最小的细节):

public class Sample
{
    private static Sample _lastSample;
    private static int _isSampling;

    public static Sample TakeSample(AutomationManager automation)
    {
        //Only start sampling if not already sampling in some other context
        if (Interlocked.CompareExchange(ref _isSampling, 0, 1) == 0)
        {
            try
            {
                Sample sample = new Sample();
                sample.PerformSampling(automation);
                _lastSample = sample;
            }
            finally
            {
                //We're done sampling
                _isSampling = 0;
            }
        }

        return _lastSample;
    }

    private void PerformSampling(AutomationManager automation)
    {
        //Lots of stuff going on that shouldn't be run in more than one context at the same time
    }
}

在我描述的场景中使用它是否安全?

【问题讨论】:

    标签: c# synchronization multithreading locking interlocked


    【解决方案1】:

    是的,它看起来很安全,因为int 在这里是一个原子类型。但我仍然建议更换

    private static int _isSampling;
    

    private static object _samplingLock = new object();
    

    并使用:

    lock(_samplingLock)
    {
        Sample sample = new Sample();
        sample.PerformSampling(automation);
       _lastSample = sample;
    }
    

    仅仅是因为它是推荐的模式,并且还确保对 _lastSample 的所有访问都得到正确处理。

    注意:我希望速度相当,lock 使用托管 Monitor 类,该类在内部使用 Interlocked。

    编辑:

    我错过了退避方面,这是另一个版本:

       if (System.Threading.Monitor.TryEnter(_samplingLock))
       {
         try
         {
             .... // sample stuff
         }
         finally
         {
              System.Threading.Monitor.Exit(_samplingLock);
         }
       }
    

    【讨论】:

    • 从维护的角度来看,TryEnter 可能更好,因为很可能更多的人会熟悉 Monitor.TryEnter 而不是 Interlocked.CompareExchange。在这种情况下,我不太担心开销,因为我每秒只采样几次。我敢肯定,当它确实执行采样时,采样过程本身的开销将比锁定原语多得多。
    • 我喜欢 Monitor.TryEnter,但我们现在不应该使用 ReaderWriterLockSlim 吗? msdn.microsoft.com/en-us/library/…
    • @Mike_g :不,只有在情况需要时。我在这里看不到。
    • 好的,现在正在使用 TryEnter; '聪明'的解决方案就这么多。 :) 另外,我无法编辑帖子,但正确的方法是 Monitor.Exit(_samplingLock)。
    • Interlocked.CompareExchange() 比给定的解决方案非常有效。你只是改变了他的要求。
    【解决方案2】:

    我通常声明一个 volatile bool 并执行以下操作:

    private volatile bool _isBusy;
    private static Sample _lastSample;
    
    private Sample DoSomething()
    {
         lock(_lastSample)
         {
           if(_isBusy)
              return _lastSample;
           _isBusy = true;
         }
    
         try
         {
           _lastSample = new sameple//do something
         }
         finally
         {
            lock(_lastSample)
            {
               _isBusy = false;
            }
         }
         return _lastSample;
    } 
    

    【讨论】:

    • 如果你只是在锁内使用它,则没有必要声明 bool volatile。此外,锁定可变字段是一种非常糟糕的做法。在您的示例中,可能同时输入了两个锁定块。当您锁定一个可变字段时,我希望有一个详细的评论,说明如果两个(甚至更多)线程同时进入锁定块,为什么它不会受到(那么多)伤害。
    猜你喜欢
    • 2011-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-25
    • 2021-10-22
    • 2013-06-07
    • 2012-02-16
    • 1970-01-01
    相关资源
    最近更新 更多