Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AsyncLock for multiple methods configuration

What I need to achieve is:

  1. When MasterAsync is executing, HumbleSlave1Async and HumbleSlave2Async can NOT.
  2. Vice Versa- When one or both of Slaves are executing, MasterAsync can NOT.
  3. Difficult part- Slaves can NOT block each other.

(Link to used AsyncLock).

    private async Task MasterAsync()
    {
        using (await _asyncLock.LockAsync())
        {
            await Task.Delay(2000);
        }
    }
    private async Task HumbleSlave1Async()
    {
        using (await _asyncLock.LockAsync())
        {
            await Task.Delay(5000);
        }
    }
    private async Task HumbleSlave2Async()
    {
        using (await _asyncLock.LockAsync())
        {
            await Task.Delay(5000);
        }
    }

I am not sure how to solve it, was thinking about use of two different locks for each slave in MasterAsync but then one lock would be in another:

    private async Task MasterAsync()
    {
        using (await _asyncLock1.LockAsync())
        {
            using (await _asyncLock2.LockAsync())
            {
                await Task.Delay(2000);
            }
        }
    }
    private async Task HumbleSlave1Async()
    {
        using (await _asyncLock1.LockAsync())
        {
            await Task.Delay(5000);
        }
    }
    private async Task HumbleSlave2Async()
    {
        using (await _asyncLock2.LockAsync())
        {
            await Task.Delay(5000);
        }
    }

Does it make sense and is it safe(deadlocks, etc...) especially when I used AsyncLock ?

like image 422
as74 Avatar asked Feb 15 '23 22:02

as74


2 Answers

When MasterAsync is executing, HumbleSlave1Async and HumbleSlave2Async can NOT. Vice Versa- When one or both of Slaves are executing, MasterAsync can NOT. Difficult part- Slaves can NOT block each other.

First, double-check whether you really want this. The majority of the time, a restructuring of the code responsibilities will simplify what synchronization you need (and is usually more efficient, too).

That said, your scenario fits a reader/writer lock. A RWL is a lock that can be taken two different ways, as a "writer" (which does not allow any other locks at the same time) or as a "reader" (which allows other readers but not writers). Stephen Toub has an async-compatible one here and I have one as part of my AsyncEx library.

Update: Example code:

private readonly AsyncReaderWriterLock _lock = new AsyncReaderWriterLock();
private async Task MasterAsync()
{
    using (await _lock.WriterLockAsync())
    {
        await Task.Delay(2000);
    }
}
private async Task HumbleSlave1Async()
{
    using (await _lock.ReaderLockAsync())
    {
        await Task.Delay(5000);
    }
}
private async Task HumbleSlave2Async()
{
    using (await _lock.ReaderLockAsync())
    {
        await Task.Delay(5000);
    }
}
like image 155
Stephen Cleary Avatar answered Feb 24 '23 08:02

Stephen Cleary


A possible problem with your approach is that when HumbleSlave2Async is executing, and MasterAsync has been called, acquired asyncLock1 and is awaiting asyncLock2, you won't be able to execute HumbleSlave1Async (because asyncLock1 is taken by MasterAsync). So, your condition #3 will not be satisfied.

Maybe you should use something like AsyncManualResetEvent to make this work.

like image 42
avo Avatar answered Feb 24 '23 06:02

avo