问题描述
我刚刚开始使用 Nito.AsyncEx 包和 AsyncLock
而不是普通的 lock() { ... }
部分,我在锁定部分中有 async
调用(因为您不能在这种情况下将 lock()
用于 good reasons 我刚刚读过)。这是我从 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
是一个 thread-safe 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
doc says:
您可以使用 already-cancelled CancellationToken 调用 Lock 或 LockAsync 尝试立即获取 AsyncLock 无需实际进入等待队列。
但我不明白如何做到这一点,至少使用同步 Lock
方法。
解决方法
我不明白该怎么做
您可以创建一个新的 const nexts = [];
par.next('li').foreach(function (elem) {
if (elem.hasClass('level-0') && nexts.length > 0)
return false; // break loop
if (elem.hasClass('level-1'))
nexts.push(elem); // add li element
});
并传递 CancellationToken
来创建一个已经取消的:
true
如果锁定已被持有,则对 using (_asyncLock.Lock(new CancellationToken(canceled: true)))
{
...
}
的调用将抛出。
也就是说,我认为这不是解决您问题的好方法。总是有可能后台作业即将完成,控制器检查锁并确定它被持有,然后后台作业释放锁。在这种情况下,控制器不会触发后台作业。
,您必须永远(!)对任何其他线程或进程做出任何假设!
在此特定示例中,您必须做的是“安排另一项工作”,除非您已经这样做了。 (为了避免“分叉炸弹”。) 那么,作业一旦真正开始执行,就必须决定:“我应该这样做吗?”如果不是,作业悄悄退出。
或者——也许这里的实际问题是:“已经有其他人≤scheduled_this_job≥?”