如何在多线程上下文中使方法独占?

时间:2008-09-29 09:23:38

标签: c# .net multithreading locking

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

我们举一个例子:

  1. 计时器过去了,所以方法是 调用。任务可能需要一些 秒。

  2. 之后,用户点击了一些 按钮,应该触发 同样的任务:BAM。它什么都不做 因为该方法已在运行。

  3. 我使用了以下解决方案:

    public void DoRecurentJob()
    {
        if(!Monitor.TryEnter(this.lockObject))
        {
            return;
        }
    
        try
        {
            // Do work
        }
        finally 
        {
            Monitor.Exit(this.lockObject);
        }
    }
    

    lockObject的声明如下:

    private readonly object lockObject = new object();
    

    编辑:只有一个对象实例包含此方法,因此我将锁定对象更新为非静态。

    有没有更好的方法呢?或者也许这个因为任何原因都是错的?

8 个答案:

答案 0 :(得分:4)

如果您只是对并行运行方法感兴趣,这看起来很合理。没有什么可以阻止它在彼此之后立即运行,比如在计时器执行Monitor.Exit()之后你按下按钮半微秒。

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

答案 1 :(得分:2)

您还可以使用MutexSemaphore,如果您希望它在跨进程中工作(会有轻微的性能损失),或者您需要设置任何其他数字而不是允许同时运行的某个线程你的代码。

还有其他信号构造可以工作,但是你的例子看起来像是诀窍,并且以简单直接的方式。

答案 2 :(得分:2)

Minor nit:如果lockObject变量是静态的,则“this.lockObject”不应该编译。虽然这是一个实例方法,但它也有一种明显的类型范围的行为,它也感觉有点奇怪(并且至少应该有大量记录)。可能使它成为一个静态方法,它以实例作为参数?

它实际上是否使用实例数据?如果没有,请将其设为静态。如果确实如此,你至少应该返回一个布尔值来说明你是否使用实例进行了工作 - 我发现很难想象我想要用特定数据完成一些工作的情况,但我不知道因为某些类似的工作是用不同的数据进行的,所以要小心是否进行了这项工作。

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

答案 3 :(得分:2)

我认为Microsoft recommends使用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();

答案 4 :(得分:1)

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

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

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

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

答案 5 :(得分:0)

这是一个很好的解决方案,虽然我对静态锁不满意。现在你不是在等待锁定,所以你不会遇到死锁问题。但是,在您下次编辑此代码时,使锁定太明显可能会让您陷入困境。此外,这不是一个非常可扩展的解决方案。

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

答案 6 :(得分:0)

更具声明性的方法是在您希望同步访问的方法上使用MethodImplOptions.Synchronized说明符:

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

但是,不建议使用此方法,原因有多种,其中大多数可以找到herehere。我发布这个,所以你不会想要使用它。在Java中,synchronized是一个关键字,因此在查看线程模式时可能会出现。

答案 7 :(得分:0)

我们有类似的要求,增加的要求是如果再次请求长时间运行的进程,它应该在当前循环完成后排队执行另一个循环。它与此类似:

https://codereview.stackexchange.com/questions/16150/singleton-task-running-using-tasks-await-peer-review-challenge

private queued = false;
private running = false;
private object thislock = new object();

void Enqueue() {
    queued = true;
    while (Dequeue()) {
        try {
            // do work
        } finally {
            running = false;
        }
    }
}

bool Dequeue() {
    lock (thislock) {
        if (running || !queued) {
            return false;
        }
        else
        {
            queued = false;
            running = true;
            return true;
        }
    }
}