如何使用 AsyncEx 检查另一个线程是否在锁定的部分内?

How to check if another thread is within a locked section, using AsyncEx?

我刚开始使用 Nito.AsyncEx package and AsyncLock instead of a normal lock() { ... } section where I have async calls within the locked section (since you can't use lock() in such cases for good reasons 我刚读到)。这是我来自 Hangfire 运行 的一份工作。我们称其为 'worker' 线程。

在另一个线程中,来自 ASP.NET 控制器,我想检查锁定部分中是否有当前正在执行的线程。如果锁定部分没有线程,那么我将通过 Hangfire 安排后台作业。如果锁定部分中已经有一个线程,那么我不想安排另一个线程。 (是的,这听起来可能有点奇怪,但那是另一回事了)。

有没有办法使用 Nito.AsyncEx 对象来检查这一点,或者我应该只在锁定部分的开头设置一个标志并在结尾取消设置?

例如我想要这个:

public async Task DoAJobInTheBackground(string queueName, int someParam)
{ 
    // do other stuff...

    // Ensure I'm the only job in this section
    using (await _asyncLock.LockAsync())
    {           
        await _aService.CallSomethingAsync());           
    }

    // do other stuff...
}

并从控制器调用的服务使用我想象的方法IsSomeoneInThereNow():

public void ScheduleAJobUnlessOneIsRunning(string queueName, int someParam)
{ 

    if (!_asyncLock.IsSomeoneInThereNow())
    {
        _backgroundJobClient.Enqueue<MyJob>(x => 
            x.DoAJobInTheBackground(queueName, someParam));
    }

}

但到目前为止我只能看到如何用一个单独的变量来做到这一点(想象 _isAnybodyInHere 是一个 或者我使用 Interlocked 代替):

public async Task DoAJobInTheBackground(string queueName, int someParam)
{ 
    // do other stuff...

    // Ensure I'm the only job in this section
    using (await _asyncLock.LockAsync())
    {
        try
        {
            _isAnybodyInHere = true; 
            await _aService.CallSomethingAsync());
        }
        finally
        {
            _isAnybodyInHere = false; 
        }
    }

    // do other stuff...
}

以及控制器调用的服务:

public void ScheduleAJobUnlessOneIsRunning(string queueName, int someParam)
{ 

    if (!_isAnybodyInHere)
    {
        _backgroundJobClient.Enqueue<MyJob>(x => 
            x.DoAJobInTheBackground(queueName, someParam));
    }

}

真的感觉应该有更好的方法。 AsyncLock doc says:

You can call Lock or LockAsync with an already-cancelled CancellationToken to attempt to acquire the AsyncLock immediately without actually entering the wait queue.

但我不知道该怎么做,至少使用同步 Lock 方法是这样。

您必须永不(!)对任何其他线程或进程做出任何假设!

在此特定示例中,您必须做的是“安排另一项工作”,除非您已经这样做了。 (避免“fork 炸弹”。) 然后,作业一旦真正开始执行,就必须决定:“我应该这样做吗?” 如果没有,作业会悄悄退出。

或者——也许这里的实际问题是:“有其他人已经≤scheduled_this_job≥吗?”

I don't understand how to do that

您可以创建一个新的 CancellationToken 并通过 true 创建一个已经取消的:

using (_asyncLock.Lock(new CancellationToken(canceled: true)))
{
  ...
}

如果锁已经持有,对 Lock 的调用将抛出。

也就是说,我认为这不是解决您的问题的好方法。总是有可能后台作业即将完成,控制器检查锁并确定它已被持有,然后后台作业释放锁。在这种情况下,控制器将不会触发后台作业。