Async Reader/Writer Lock
AsyncReaderWriterLock is a non-blocking asynchronous alternative to ReaderWriteLockSlim with the same semantics.
This class supports methods to determine whether locks are held or contended. These methods are designed for monitoring system state, not for synchronization control.
The reader lock and writer lock both support interruption during lock acquisition using CancellationToken.
using System.Threading;
using DotNext.Threading;
var rwlock = new AsyncReaderWriterLock();
await rwlock.EnterReadLockAsync(CancellationToken.None);
try
{
//reader stuff here
}
finally
{
rwlock.Release();
}
await rwlock.EnterWriteLockAsync(TimeSpan.FromSecond(2));
try
{
//writer stuff here
}
finally
{
rwlock.Release();
}
await rwlock.UpgradeToWriteLockAsync(TimeSpan.FromSecond(2), CancellationToken.None);
try
{
//writer stuff here
}
finally
{
rwlock.DowngradeFromWriteLock();
}
//or with 'using statement'
using (await rwlock.AcquireReadLockAsync(CancellationToken.None))
{
//reader stuff here
}
using (await rwlock.AcquireWriteLockAsync(TimeSpan.FromSecond(2)))
{
//writer stuff here
}
Exclusive lock should be destroyed if no longer needed by calling Dispose
method which is not thread-safe.
Behavior of UpgradeToWriteLockAsync
and DowngradeFromWriteLock
methods is the same as in ReaderWriterLock. You need to be in the read lock to call the upgrade. After calling of DowngradeFromWriteLock
method the current flow keeping the read lock so you need to call Release
method to release the lock completely.
Acquisition Order
This lock does not impose a reader or writer preference ordering for lock access. However, it respects fairness policy. It means that callers contend for entry using an approximately arrival-order policy. When the currently held lock is released either the longest-waiting single writer will be assigned the write lock, or if there is a group of readers waiting longer than all waiting writers, that group will be assigned the read lock.
A caller that tries to acquire a read lock (non-reentrant) will enqueued if either the write lock is held, or there is a waiting writer. The caller will not acquire the read lock until after the oldest currently waiting writer has acquired and released the write lock. Of course, if a waiting writer abandons its wait, leaving one or more readers as the longest waiters in the queue with the write lock free, then those readers will be assigned the read lock.
A caller that tries to acquire a write lock (non-reentrant) will block unless both the read lock and write lock are free (which implies there are no waiters).
Graceful Shutdown
Dispose
method is not thread-safe and may cause unpredictable behavior if called on the lock which was acquired previously. This is happening because the method doesn't wait for the lock to be released. Starting with version 2.6.0 this type of lock implements IAsyncDisposable interface and provides a way for graceful shutdown. DisposeAsync
behaves in the following way:
- If lock is not acquired then completes synchronously
- If lock is acquired then suspends the caller and wait when it will be released, then dispose the lock
Synchronous acquisition
The class exposes TryEnterReadLock(TimeSpan)
and TryEnterWriteLock(TimeSpan)
blocking methods that can be used by synchronous callers. The method allows to perform mixed synchronization for synchronous and asynchronous code at the same time.