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

C# 如何避免在不阻塞的情况下并发调用方法?,c#,multithreading,synchronization,locking,nonblocking,C#,Multithreading,Synchronization,Locking,Nonblocking,我想运行一个可能运行几秒钟的清理任务。多个线程可以调用此任务,但我只想运行此任务一次。所有其他调用都应该跳过 下面是我当前的实现,但我无法想象在.net框架中没有更好的解决方案,导致代码行数减少 object taskLock; bool isRunning; void Task() { if (isRunning) return; try { lock (taskLock)

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


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

    object taskLock;
    bool isRunning;

    void Task()
    {
        if (isRunning) return;

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

是的,有更好的解决办法。您可以使用,代码变得更简单且无锁:

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
    }
}
在这种情况下,
互锁。CompareExchange
作为原子操作(伪代码)执行以下操作:

从MSDN文档中:

public static int CompareExchange(
    ref int location1,
    int value,
    int comparand
)
如果comparand和位置1中的值相等,则值为 存储在位置1。否则,不执行任何操作。比较 交换操作作为原子操作执行。这个 CompareExchange的返回值是位置1中的原始值, 无论交换是否发生


您可以使用类似的方法,但使用并发字典代替taskLock对象。tbh-如果它目前有效,继续并分解积压工作中的其他项目“我无法想象没有更好的解决方案”这是一个非正统的问题,因为根据定义,它充满了并发歧义。根据时间的不同,任务可能只运行一次,或者对每个调用线程运行一次。至少,它是低效的。是的,它可以是非常低效的,但我看不到一个更好的替代信号,为某项任务的发生。另一种方法是使用重置事件,任务只等待另一个线程设置事件,但我必须管理一个线程/任务。我喜欢这个解决方案,但CompareExchange不是比
if(isRunning)返回慢吗?如果(isRunning==1 | | Interlocked.compareeexchange(ref isRunning,1,0)==1)返回,则可能执行
if(isRunning==1 | | Interlocked.compareeexchange可能会更快not@RamonSmits是的,它会更快,但是如果(isRunning==0&&Interlocked),它应该是
。比较交换(ref isRunning,1,0)==0)
,您需要将
isRunning
声明为volatile。另一方面,
互锁。CompareExchange
非常快。“我不相信你会得到可观的收益。”拉蒙斯密茨说。我编辑了我的答案以包含优化。这里有一个微基准测试,比较优化为什么需要“volatile”关键字?@RamonSmits,因为它可以被其他线程修改。它可以防止编译器执行一些可能导致错误结果的优化。它与记忆栅栏有关
public static int CompareExchange(
    ref int location1,
    int value,
    int comparand
)