如何使方法在多线程上下文中互斥?

时间:2020-03-06 14:52:04  来源:igfitidea点击:

我有一种应以独占方式执行的方法。基本上,它是一个多线程应用程序,其中该方法由计时器定期调用,但也可以由用户操作手动触发。

让我们举个例子:

  • 计时器过去了,因此调用了该方法。该任务可能需要几秒钟。
  • 紧接着,用户单击某个按钮,这将触发相同的任务:BAM。因为该方法已经在运行,所以它什么也不做。

我使用以下解决方案:

public void DoRecurentJob()
{
    if(!Monitor.TryEnter(this.lockObject))
    {
        return;
    }

    try
    {
        // Do work
    }
    finally 
    {
        Monitor.Exit(this.lockObject);
    }
}

像这样声明lockObject的地方:

private readonly object lockObject = new object();

编辑:将只有对象的一个​​实例保存此方法,所以我将锁对象更新为非静态。

有更好的方法吗?或者,也许这是出于任何原因是错误的?

解决方案

如果我们只是不想让该方法并行运行,这看起来是合理的。没有什么可以阻止它彼此立即运行的,比如说我们在计时器执行Monitor.Exit()后半微秒按下了按钮。

将锁对象设置为只读静态也很有意义。

如果希望跨进程工作(有一点性能损失),或者需要设置除运行代码段所允许的并发线程之一之外的任何其他数字,则也可以使用" Mutex"或者" Semaphore"。

还有其他一些信令构造也可以使用,但是示例看起来很简单,方式就很有效。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

次要提示:如果lockObject变量是静态的,则不应编译" this.lockObject"。尽管这是一个实例方法,但它也具有明显的类型范围的行为,这也让人感到有些奇怪(至少应该有大量文献记录)。可能使其成为一个以实例为参数的静态方法?

它实际上是否使用实例数据?如果不是,请将其设置为静态。如果是这样,我们至少应该返回一个布尔值,以说我们是否对实例进行了处理,我很难想象我想要对特定数据进行某些处理的情况,但是我不在乎如果由于使用不同的数据执行了一些类似的工作而没有执行该工作。

我认为应该可以,但是确实有点奇怪。我一般不喜欢使用手动锁定,只是因为它很容易出错,但这看起来还可以。 (我们需要考虑" if"和" try"之间的异步异常,但我怀疑它们不会成为问题,我不记得CLR做出的确切保证。)

代码很好,但是可以更好地传达意图,因此同意将方法更改为静态。奇怪的是,一个类的所有实例之间都有一个可以同步运行的方法,但是该方法不是静态的。

请记住,我们始终可以将静态同步方法保护或者私有,使其仅对类的实例可见。

public class MyClass
{ 
    public void AccessResource()
    {
        OneAtATime(this);
    }

    private static void OneAtATime(MyClass instance) 
    { 
       if( !Monitor.TryEnter(lockObject) )
       // ...

尽管我对静态锁并不十分满意,但这是一个很好的解决方案。现在,我们无需等待锁定,因此不会遇到死锁的麻烦。但是,使锁变得过于可见很容易使我们在下次必须编辑此代码时遇到麻烦。而且,这不是一个非常可扩展的解决方案。

我通常会尝试使所有要保护的资源不会被多个线程访问,而一个类的私有实例变量然后又将一个锁作为私有实例变量。这样,我们可以在需要缩放时实例化多个对象。

我认为Microsoft建议使用lock语句,而不是直接使用Monitor类。它提供了一个更整洁的布局,并确保在所有情况下都可以释放锁。

public class MyClass
{

  // Used as a lock context
  private readonly object myLock = new object();

  public void DoSomeWork()
  {
    lock (myLock)
    {
      // Critical code section
    }
  }
}

如果应用程序要求锁跨越MyClass的所有实例,则可以将锁上下文定义为静态字段:

private static readonly object myLock = new object();

更具声明性的方法是在要与之同步访问的方法上使用MethodImplOptions.Synchronized说明符:

[MethodImpl(MethodImplOptions.Synchronized)] 
public void OneAtATime() { }

但是,不建议使用此方法,原因有很多,其中大多数都可以在此处和此处找到。我发布此消息是为了让我们不会想使用它。在Java中," synchronized"是一个关键字,因此在检查线程模式时可能会出现它。