https://github.com/MarkCiliaVincenti/AsyncKeyedLock
An asynchronous .NET library that allows you to lock based on a key (keyed semaphores), limiting concurrent threads sharing the same key to a specified number, with optional pooling for reducing memory allocations.
https://github.com/MarkCiliaVincenti/AsyncKeyedLock
async duplicate key keyed lock pooling semaphore semaphores semaphoreslim synchronization
Last synced: about 2 months ago
JSON representation
An asynchronous .NET library that allows you to lock based on a key (keyed semaphores), limiting concurrent threads sharing the same key to a specified number, with optional pooling for reducing memory allocations.
- Host: GitHub
- URL: https://github.com/MarkCiliaVincenti/AsyncKeyedLock
- Owner: MarkCiliaVincenti
- License: mit
- Created: 2021-11-22T09:00:26.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2026-01-02T15:54:25.000Z (2 months ago)
- Last Synced: 2026-01-14T08:53:09.646Z (about 2 months ago)
- Topics: async, duplicate, key, keyed, lock, pooling, semaphore, semaphores, semaphoreslim, synchronization
- Language: C#
- Homepage: https://www.nuget.org/packages/AsyncKeyedLock
- Size: 453 KB
- Stars: 299
- Watchers: 8
- Forks: 17
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# Â AsyncKeyedLock
[](https://actions-badge.atrox.dev/MarkCiliaVincenti/AsyncKeyedLock/goto?ref=master) [](https://www.nuget.org/packages/AsyncKeyedLock) [](https://www.nuget.org/packages/AsyncKeyedLock) [](https://app.codacy.com/gh/MarkCiliaVincenti/AsyncKeyedLock/dashboard) [](https://app.codecov.io/gh/MarkCiliaVincenti/AsyncKeyedLock)
An asynchronous .NET library that allows you to lock based on a key (keyed semaphores), limiting concurrent threads sharing the same key to a specified number, with optional pooling for reducing memory allocations.
For example, suppose you were processing financial transactions, but while working on one account you wouldn't want to concurrently process a transaction for the same account. Of course, you could just add a normal lock, but then you can only process one transaction at a time. If you're processing a transaction for account A, you may want to also be processing a separate transaction for account B. That's where AsyncKeyedLock comes in: it allows you to lock but only if the key matches.
The library uses two very different methods for locking, `AsyncKeyedLocker` which uses an underlying `ConcurrentDictionary` that's cleaned up after use and `StripedAsyncKeyedLocker` which uses a technique called striped locking. Both have their advantages and disadvantages, and in order to help you choose you are highly recommended to read about it in the [wiki](https://github.com/MarkCiliaVincenti/AsyncKeyedLock/wiki).
A simple non-keyed lock is also available through [`AsyncNonKeyedLocker`](https://github.com/MarkCiliaVincenti/AsyncKeyedLock/wiki/How-to-use-AsyncNonKeyedLocker).
## Installation and usage
Using this library is straightforward. Here's a simple example for using `AsyncKeyedLocker`:
```csharp
private static readonly AsyncKeyedLocker _asyncKeyedLocker = new();
...
using (await _asyncKeyedLocker.LockAsync("test123"))
{
...
}
```
or with timeouts:
```csharp
private static readonly AsyncKeyedLocker _asyncKeyedLocker = new();
private const _timeout = 100;
...
using (var releaser = await _asyncKeyedLocker.LockOrNullAsync("test123", _timeout))
{
if (releaser is not null)
{
...
}
}
```
This libary also supports conditional locking, whether for `AsyncKeyedLocker`, `StripedAsyncKeyedLocker` or `AsyncNonKeyedLocker`. This could provide a workaround for `reentrancy` in some scenarios for example in recursion:
```csharp
double factorial = Factorial(number);
public static double Factorial(int number, bool isFirst = true)
{
using (await _asyncKeyedLocker.ConditionalLockAsync("test123", isFirst))
{
if (number == 0)
return 1;
return number * Factorial(number-1, false);
}
}
```
For more help with `AsyncKeyedLocker`, or for examples with `StripedAsyncKeyedLocker` or `AsyncNonKeyedLocker` (for simple, non-keyed locking), please [take a look at our wiki](https://github.com/MarkCiliaVincenti/AsyncKeyedLock/wiki).
## Pooling change in v7.0.0
Prior to AsyncKeyedLock 7.0.0, pooling was disabled by default and you needed to specify a pool size in order to enable it. Since v7.0.0, pooling is enabled by default with an initial fill of 1 and a pool size of 20. In order to disable pooling (not recommended), you need to now specify a pool size of 0 within AsyncKeyedLockOptions. Read more about pooling in [our wiki](https://github.com/MarkCiliaVincenti/AsyncKeyedLock/wiki/How-to-use-AsyncKeyedLocker#pooling).
## Non-generic constructor removal in v8.0.0
As of v8.0.0, the non-generic constructors for `AsyncKeyedLocker` have been removed. These were already marked as obsolete; if you were using them, please switch to `AsyncKeyedLocker`.
## Benchmarks
This library has been extensively benchmarked against several other options and [our benchmarks](https://github.com/MarkCiliaVincenti/AsyncKeyedLock/wiki/Benchmarks) run publicly and transparently on Github Actions.
## Credits
Check out our [list of contributors](https://github.com/MarkCiliaVincenti/AsyncKeyedLock/blob/master/CONTRIBUTORS.md)!