0

我刚刚开始使用Nito.AsyncEx包,AsyncLock而不是在锁定部分lock() { ... }中有调用的普通部分(因为我刚刚读到的充分理由async,你不能lock()在这种情况下使用)。这是我从 Hangfire 运行的一项工作。让我们称之为“工人”线程。

在另一个线程中,来自 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是一个线程安全的 bool或者我使用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

您可以使用已取消的 CancellationToken调用 Lock 或 LockAsync 以尝试立即获取 AsyncLock 而无需实际进入等待队列。

但我不明白如何做到这一点,至少使用同步Lock方法。

4

2 回答 2

1

我不明白该怎么做

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

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

Lock如果锁已被持有,调用将抛出。

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

于 2021-03-30T21:05:37.303 回答
0

您绝不能(!)对任何其他线程或进程做出任何假设!

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

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

于 2021-03-30T20:09:36.857 回答