【问题标题】:Exit a method if another thread is executing it如果另一个线程正在执行某个方法,则退出该方法
【发布时间】:2012-09-14 03:18:50
【问题描述】:

我在多线程应用程序中有一个方法,我希望在调用此方法时出现以下行为:

  1. 如果当前没有其他线程正在执行该方法,则执行它。
  2. 如果另一个线程当前正在执行该方法,则退出该方法而不执行它。

C# 中的lock 语句对于等待线程完成执行很有用,但我不想序列化对该方法的访问,而是在另一个线程执行时绕过执行所述方法。

【问题讨论】:

  • 收藏了这个。之前,我试图通过创建一个循环该方法的线程并使用volatile boollockAutoResetEvent 的“hail mary”组合来解决这个问题。 (吐在嘴里……)

标签: c# thread-safety


【解决方案1】:

您可以使用 Monitor.TryEnter 执行此操作,但可能更简单:互锁:

int executing; // make this static if you want this one-caller-only to
               // all objects instead of a single object
void Foo() {
    bool won = false;
    try {
        won = Interlocked.CompareExchange(ref executing, 1, 0) == 0;
        if(won) {
           // your code here
        }
    } finally {
        if(won) Interlocked.Exchange(ref executing, 0);
    }

}

【讨论】:

  • @JoachimIsaksson 用于无锁编码,Interlocked 是主要的首选武器。事实上,它是编译器用来在类字段事件中实现线程安全的。
  • +1 表示Interlocked。出于某种原因,Monitor.TryEnter 在返回 false 时非常慢。
  • 我也+1。如果Monitor.TryEnter() 被证明是一个问题,这是正确的方法(并不是说Monitor.TryEnter()更多正确的)。
【解决方案2】:

我想我不明白...如果一次只能由一个线程调用,为什么要多个线程调用它?

无论如何,你可以使用Monitor.TryEnter()。如果获取锁失败,它不会阻塞并返回false。在这种情况下,您可以从函数中返回。

【讨论】:

  • 它可以在任何时候被任何线程调用,但是如果它被线程A调用并且线程B正在执行它,那么线程A应该退出方法调用而不是执行一次线程 B 结束。
  • @ScottMitchell +1。在我的例子中,方法的业务逻辑是这样的,如果线程 B 在 A 执行时调用,我知道线程 A 的方法调用的操作将满足线程 B 的结果。例如,我的方法基本上调用了一个存储过程更新一堆记录的数据库。 (我不想在从多个线程调用该存储过程时一遍又一遍地运行该存储过程,因为 I/O 很昂贵)
【解决方案3】:

在其他地方创建布尔变量,在启动方法时设置为真,在退出方法时设置为假,在运行方法之前检查变量是否为假然后运行否则退出方法

【讨论】:

  • 如果要被不同的线程共享,变量需要是静态的。
  • @Oded 除非所有线程都可以访问同一个实例,或者只需要阻止线程在同一个实例上同时运行该方法。
  • @Servy - 很公平,虽然我没有看到控股类是单例或静态类。
  • @Oded 也不需要;只是线程之间共享的一个实例。这很容易发生;闭包是一种常见的方式。
【解决方案4】:

这是一个用于此目的的辅助方法:

static class Throttle
{
    public static void RunExclusive(ref int isRunning, Action action)
    {
        if (isRunning > 0) return;

        bool locked = false;
        try
        {
            try { }
            finally
            {
                locked = Interlocked.CompareExchange(ref isRunning, 1, 0) == 0;
            }

            if (locked) action();
        }
        finally 
        { 
            if (locked) 
                Interlocked.Exchange(ref isRunning, 0); 
        }
    }
}

并像这样使用它:

private int _isTuning = 0;
private void Tune() { ... }

...

Throttle.RunExclusive(ref _isTuning, Tune);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-29
    • 2020-10-11
    • 1970-01-01
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    相关资源
    最近更新 更多