2008-09-29 22 views
3

我有一个方法应该以独家方式执行。基本上,这是一个多线程应用程序,其中该方法由定时器定期调用,但也可以由用户操作手动触发。如何在多线程上下文中独占方法?

让我们举个例子:

  1. 的定时器结束,因此该方法是 调用。该任务可能需要几秒钟的时间。

  2. 之后,用户点击一些 按钮,这应该会触发 相同的任务: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(); 

编辑:将有哪些持有该对象的只有一个实例方法,所以我将锁对象更新为非静态的。

有没有更好的方法来做到这一点?或者,也许这是一个错误的任何理由?

回答

4

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

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

2

如果您希望它跨越进程(略有性能损失),或者您需要设置任何其他数字,而不是允许的并发线程运行您的代码段,您也可以使用MutexSemaphore

还有其他的信令结构可以工作,但你的例子看起来像它的伎俩,并以一种简单而直接的方式。

+0

注意的是互斥使用内核资源重量级的对象,如果你需要输入锁跨进程的工作应仅使用。显示器更轻更快。在'Accelerated C#2008'中,Trey Nash写道:“一个简单的测试表明,使用Mutex的时间比Monitor类长34倍。” – 2008-09-29 20:24:17

+0

优化速度听起来非常不成熟。 – bzlm 2008-10-01 11:50:09

2

小问题:如果lockObject变量是静态的,那么“this.lockObject”不应该编译。它也有点奇怪(至少应该大量记录),虽然这是一种实例方法,但它也具有明显的类型行为。可能使它成为一个以实例为参数的静态方法?

它实际使用实例数据吗?如果没有,请将其设为静态。如果是这样,你至少应该返回一个布尔值来表示你是否对这个实例进行了工作 - 我发现很难想象这种情况,我希望用某个特定的数据完成一些工作,但是我没有请注意,如果因为一些类似的工作正在使用不同的数据执行某项工作而未执行该工作。

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

+0

你对“这个”完全正确。前缀,我复制/粘贴了我的代码后添加了它:“糟糕,我应该更好地将前缀lockObject设置为”this“,以使我的示例对于计算器读者更加清晰:) :) – 2008-09-29 09:57:22

1

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

请记住,您始终可以将静态同步方法设置为受保护或私有方式,并且只允许该类的实例可见。

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

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

这是一个很好的解决方案,虽然我对静态锁并不满意。现在你不会等待锁定,所以你不会陷入死锁的困境。但是,在下次编辑此代码时,使锁显示可​​能很容易让您陷入困境。这也不是一个可扩展的解决方案。

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

1

我认为微软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的所有情况下,您可以定义为静态字段的锁定方面:这样做是利用在MethodImplOptions.Synchronized符的

private static readonly object myLock = new object(); 
0

更声明式的方式该方法以你希望同步访问:

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

然而,这种方法不鼓励由于几个原因,其中大部分可以找到herehere。我发布这个,所以你不会觉得很想用它。在Java中,​​是一个关键字,所以在查看线程模式时可能会出现。

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; 
     } 
    } 
} 
相关问题