AsyncKeyedLock

repository·master·Indexed 43 days ago

https://github.com/markciliavincenti/asynckeyedlock

An asynchronous .NET library providing keyed semaphores to limit concurrency based on specific keys. It features AsyncKeyedLocker<TKey> for granular locking, StripedAsyncKeyedLocker for striped locking, and AsyncNonKeyedLocker for non-keyed async locks. Supports timeouts via LockOrNullAsync, conditional locking for reentrancy, and memory allocation pooling as of v7.0.0.

Tokens
906
Snippets
3
Records
6
Agent score
6%

What's inside AsyncKeyedLock

  1. Understand the different locker types

    master

    AsyncKeyedLock provides different implementations depending on your performance and memory requirements:

    1. AsyncKeyedLocker<TKey>: Uses an underlying ConcurrentDictionary that is cleaned up after use. This is the standard implementation for keyed locking.
    2. StripedAsyncKeyedLocker: Uses a striped locking technique. This is an alternative approach with different performance characteristics.
    3. AsyncNonKeyedLocker: Provides a simple, non-keyed asynchronous lock (similar to a standard semaphore/lock but async-compatible).
  2. Install and use AsyncKeyedLock

    master

    AsyncKeyedLock is an asynchronous .NET library for keyed semaphores. It allows you to limit concurrent threads sharing the same key to a specified number, enabling granular locking (e.g., locking per account ID rather than a single global lock).

    To use AsyncKeyedLocker<TKey>, instantiate it and use LockAsync to acquire a lock for a specific key. The lock is released when the returned object is disposed via a using block.

    private static readonly AsyncKeyedLocker<string> _asyncKeyedLocker = new();
    
    // Basic usage
    using (await _asyncKeyedLocker.LockAsync("test123"))
    {
      // Critical section for key "test123"
    }
  3. Use conditional locking for reentrancy scenarios

    master

    The library supports conditional locking via ConditionalLockAsync. This is useful for managing reentrancy, such as in recursive functions, where you might want to acquire a lock only on the first call and skip it on subsequent recursive calls.

    public static double Factorial(int number, bool isFirst = true)
    {
      // Only locks if 'isFirst' is true
      using (await _asyncKeyedLocker.ConditionalLockAsync("test123", isFirst))
      {
        if (number == 0)
          return 1;
        return number * Factorial(number - 1, false);
      }
    }
  4. Acquire locks with timeouts using LockOrNullAsync

    master

    If you want to attempt to acquire a lock but avoid waiting indefinitely, use LockOrNullAsync. This method accepts a timeout. If the lock cannot be acquired within the timeout, it returns null instead of throwing or waiting further.

    private static readonly AsyncKeyedLocker<string> _asyncKeyedLocker = new();
    private const int _timeout = 100;
    
    // Attempt to acquire lock with a timeout
    using (var releaser = await _asyncKeyedLocker.LockOrNullAsync("test123", _timeout))
    {
      if (releaser is not null)
      {
        // Lock acquired successfully
      }
      else
      {
        // Timeout reached, lock not acquired
      }
    }