如何避免在不阻塞的情况下并发调用方法

本文关键字:情况下 并发 调用 方法 何避免 | 更新日期: 2023-09-27 18:35:44

我想运行一个可能运行几秒钟的清理任务。多个线程可以调用此任务,但我只想运行此任务一次。所有其他调用都应跳过。

以下是我当前的实现,但我无法想象 .net 框架中没有更好的解决方案,导致代码行数减少。

    object taskLock;
    bool isRunning;
    void Task()
    {
        if (isRunning) return;
        try
        {
            lock (taskLock)
            {
                if (isRunning) return;
                isRunning = true;
            }
            // Perform the magic
        }
        finally
        {
            isRunning = false;
        }
    }

如何避免在不阻塞的情况下并发调用方法

是的,有一个更好的解决方案。您可以使用Interlocked.CompareExchange,代码变得更加简单和无锁:

class Worker
{
    private volatile int isRunning = 0;
    public void DoWork()
    {
        if (isRunning == 0 && Interlocked.CompareExchange(ref isRunning, 1, 0) == 0)
        {
            try
            {
                DoTheMagic();
            }
            finally
            {
                isRunning = 0;
            }
        }
    }
    private void DoTheMagic()
    {
        // do something interesting
    }
}

在这种情况下,Interlocked.CompareExchange作为原子操作(伪代码)执行以下操作:

wasRunning = isRunning;
if isRunning = 0 then 
     isRunning = 1
end if
return wasRunning

从 MSDN 文档:

public static int CompareExchange(
    ref int location1,
    int value,
    int comparand
)

如果比较和位置 1 中的值相等,则值为 存储在位置 1。否则,不执行任何操作。比较 交换操作作为原子操作执行。这 比较交换的返回值是位置 1 中的原始值, 是否进行交换