Skip to content

soenneker/soenneker.asyncs.locks

Soenneker.Asyncs.Locks

The fastest .NET async lock

This library provides a single primitive: AsyncLock.

Design goal

AsyncLock is built to be the fastest possible correct mutex for real-world .NET systems.

It provides the following guarantees:

Cancellation-safe

  • Fully supports cancellation before acquisition and while waiting for both async and sync callers.
  • Cancelled waiters are removed immediately, never resumed, and never leaked — with zero impact on the fast path.

Unified async + sync locking

  • Async and synchronous callers share the same mutex.
  • Ordering is preserved without adapters, wrappers, or duplicated synchronization primitives.

Performance (by design)

  • Uncontended acquisition is as close to a single atomic operation as possible
  • No allocations, tasks, or state machines unless contention occurs
  • Cancellation and disposal logic are completely excluded from the fast path
  • Deterministic behavior under contention

Installation

dotnet add package Soenneker.Asyncs.Locks

Usage

Async

await using (await _lock.Lock(ct))
{
    // critical section
}

Sync

using (_lock.LockSync())
{
    // critical section
}

Try-lock

if (_lock.TryLock(out var releaser))
{
    using (releaser)
    {
        // critical section
    }
}

Benchmarks

Async lock acquisition

Method Mean Error StdDev Median Ratio Allocated
Soenneker.Asyncs.Lock 10.06 ns 0.212 ns 0.393 ns 10.01 ns baseline -
SemaphoreSlim 19.17 ns 0.406 ns 0.360 ns 19.10 ns 1.91x slower -
Nito.AsyncEx.AsyncLock 55.32 ns 1.078 ns 2.645 ns 54.81 ns 5.51x slower 320 B

Synchronous lock acquisition

Method Mean Error StdDev Median Ratio Allocated
Soenneker.Asyncs.Lock 8.09 ns 0.179 ns 0.314 ns 8.03 ns baseline -
SemaphoreSlim 19.49 ns 0.403 ns 0.727 ns 19.09 ns 2.41x slower -
Nito.AsyncEx.AsyncLock 48.43 ns 1.005 ns 2.915 ns 48.05 ns 6.00x slower 320 B

Sponsor this project

  •  

Packages

 
 
 

Contributors

Languages