I've just started using 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 I've just read about). This is within a job that I'm running from Hangfire. Let's call this the 'worker' thread.
In another thread, from an ASP.NET controller, I'd like to check if there's a thread that's currently executing within the locked section. If there's no thread in the locked section then I'll schedule a background job via Hangfire. If there's already a thread in the locked section then I don't want to schedule another one. (Yes, this might sound a little weird, but that's another story).
Is there a way to check this using the Nito.AsyncEx
objects, or should I just set a flag at the start of the locked section and unset it at the end?
e.g. I'd like to this:
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...
}
and from a service called by a controller use my imaginary method IsSomeoneInThereNow()
:
public void ScheduleAJobUnlessOneIsRunning(string queueName, int someParam)
{
if (!_asyncLock.IsSomeoneInThereNow())
{
_backgroundJobClient.Enqueue<MyJob>(x =>
x.DoAJobInTheBackground(queueName, someParam));
}
}
but so far I can only see how to do this with a separate variable (imagining _isAnybodyInHere
is a thread-safe bool or I used Interlocked
instead):
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...
}
and from a service called by a controller:
public void ScheduleAJobUnlessOneIsRunning(string queueName, int someParam)
{
if (!_isAnybodyInHere)
{
_backgroundJobClient.Enqueue<MyJob>(x =>
x.DoAJobInTheBackground(queueName, someParam));
}
}
Really it feels like there should be a better way. The 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.
but I don't understand how to do that, at least using the synchronous Lock
method.