SonSa
SonSa

Reputation: 31

C# async method called concurrently should wait for the first operation to complete

I have a method that looks like

public async Task<OpenResult> openAsync()

I want to do something like if there is a current call to openAsync in the process of getting executed, I would like to place any calls to OpenAsync be added to a queue.

When the first call completes, I want to complete all the ones in the queue with the result of the first call.

What’s the way to achieve this in C#

Upvotes: 3

Views: 199

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062855

Usually, this kind of detail is left to the caller, i.e. by making the caller await appropriately and only call methods when they should call methods. However, if you must do this, one simple way is via a semaphore; consider:

class HazProtected
{
    private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);

    public async Task<OpenResult> OpenAsync(CancellationToken cancellationToken = default)
    {
        await _lock.WaitAsync(cancellationToken);
        try
        {
            return await DoOpenAsync(cancellationToken);
        }
        finally
        {
            _lock.Release();
        }
    }

    private async Task<OpenResult> DoOpenAsync(CancellationToken cancellationToken)
    {
        // ... your real code here
    }
}

The code in OpenAsync ensures that only one concurrent async caller can be attempting to open it at a time. When there is a conflict, callers are held asynchronously until the semaphore can be acquired. There is a complication, though; SempahoreSlim has some known problems on .NET Framework (resolved in .NET Core) when there are both asynchronous and synchronous semaphore acquisitions at the same time - which can lead to a spiral of death.


In more complex scenarios, it is possible to write your own queue of pending callers; this is a very very exotic scenario and should usually not be attempted unless you understand exactly why you're doing it!

Upvotes: 2

Related Questions