Skip to content

Instantly share code, notes, and snippets.

@pmunin
Created March 22, 2019 19:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pmunin/acb77870ffa28e6d26b4c0ea97506361 to your computer and use it in GitHub Desktop.
Save pmunin/acb77870ffa28e6d26b4c0ea97506361 to your computer and use it in GitHub Desktop.
AsyncLock
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncLockExtensions
{
/// <summary>
/// Virtual critical section for async function.
/// Monitor.Enter/Exit and lock{} does not work for async methods and cause deadlocks and exceptions. AsyncLock solves these issues. Implementation copied from Microsoft.EntityFrameworkCore.Internal.AsyncLock (.nuget\packages\microsoft.entityframeworkcore\2.1.4\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll)
/// <seealso cref="https://github.com/neosmart/AsyncLock"/>
/// <example>
/// using(await _myLock.LockAsync())
/// {
/// //critical section code
/// }
/// </example>
/// </summary>
public class AsyncLock
{
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
private readonly AsyncLock.Releaser _releaser;
private readonly Task<AsyncLock.Releaser> _releaserTask;
public AsyncLock()
{
this._releaser = new AsyncLock.Releaser(this);
this._releaserTask = Task.FromResult<AsyncLock.Releaser>(this._releaser);
}
public Task<AsyncLock.Releaser> LockAsync(CancellationToken cancellationToken = default(CancellationToken))
{
Task task = this._semaphore.WaitAsync(cancellationToken);
if (!task.IsCompleted)
return task.ContinueWith<AsyncLock.Releaser>((Func<Task, object, AsyncLock.Releaser>)((_, state) => ((AsyncLock)state)._releaser), (object)this, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
return this._releaserTask;
}
public AsyncLock.Releaser Lock()
{
this._semaphore.Wait();
return this._releaser;
}
public struct Releaser : IDisposable
{
private readonly AsyncLock _toRelease;
internal Releaser(AsyncLock toRelease)
{
this._toRelease = toRelease;
}
public void Dispose()
{
this._toRelease._semaphore.Release();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment