Soenneker.Asyncs.Locks 4.0.74

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package Soenneker.Asyncs.Locks --version 4.0.74
                    
NuGet\Install-Package Soenneker.Asyncs.Locks -Version 4.0.74
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Soenneker.Asyncs.Locks" Version="4.0.74" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Soenneker.Asyncs.Locks" Version="4.0.74" />
                    
Directory.Packages.props
<PackageReference Include="Soenneker.Asyncs.Locks" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Soenneker.Asyncs.Locks --version 4.0.74
                    
#r "nuget: Soenneker.Asyncs.Locks, 4.0.74"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Soenneker.Asyncs.Locks@4.0.74
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Soenneker.Asyncs.Locks&version=4.0.74
                    
Install as a Cake Addin
#tool nuget:?package=Soenneker.Asyncs.Locks&version=4.0.74
                    
Install as a Cake Tool

alternate text is missing from this package README image alternate text is missing from this package README image alternate text is missing from this package README image alternate text is missing from this package README image

Soenneker.Asyncs.Locks

A low-allocation mutex shared by asynchronous and synchronous callers.

AsyncLock provides cancellable async acquisition, blocking synchronous acquisition, and a non-blocking try-lock. Contended async waiters use pooled IValueTaskSource instances, while the uncontended path avoids allocating a Task.

Installation

dotnet add package Soenneker.Asyncs.Locks

Asynchronous locking

Keep the lock on the object that owns the protected state and dispose every acquired Releaser:

using Soenneker.Asyncs.Locks;

public sealed class BalanceStore : IAsyncDisposable
{
    private readonly AsyncLock _lock = new();
    private decimal _balance;

    public async ValueTask Add(
        decimal amount,
        CancellationToken cancellationToken)
    {
        using Releaser releaser = await _lock.Lock(cancellationToken);
        _balance += amount;
    }

    public ValueTask DisposeAsync() => _lock.DisposeAsync();
}

Releaser implements IDisposable, not IAsyncDisposable, so use using for the acquired token even inside an async method. await using is appropriate for the AsyncLock itself when the owner is disposed asynchronously.

The tokenless overload avoids cancellation registration when cancellation is not needed:

using Releaser releaser = await asyncLock.Lock();

Synchronous locking

Synchronous and asynchronous callers contend for the same mutex:

using Releaser releaser = asyncLock.LockSync(cancellationToken);
// Protected synchronous work

LockSync blocks the calling thread while contended. Do not use it from asynchronous request paths merely to avoid await; use Lock there.

Try without waiting

if (asyncLock.TryLock(out Releaser releaser))
{
    using (releaser)
    {
        // The lock is held here.
    }
}
else
{
    // Another caller holds or is waiting for the lock, or it was disposed.
}

TryLock returns false instead of waiting. After disposal it also returns false; the blocking acquisition methods throw ObjectDisposedException.

Cancellation

Lock(CancellationToken) and LockSync(CancellationToken) support cancellation before acquisition and while queued. A cancelled waiter does not enter the critical section. Once acquisition succeeds, cancellation does not release the lock; only disposing the returned Releaser does that.

Always keep acquisition outside the protected try/finally or using body so code does not attempt to release a lock it never acquired.

Disposal

The two disposal methods intentionally differ:

Method Current holder Queued and future callers
Dispose() May finish and release normally; disposal does not wait. Queued callers fail and future blocking acquisitions throw.
DisposeAsync() Waits for the current holder to release. Queued callers fail and future blocking acquisitions throw.

Neither method forcibly interrupts code already inside the critical section. Dispose the lock only when its owning service is shutting down and no new work should be accepted.

Correctness rules

  • AsyncLock is not reentrant. Code that already holds it must not acquire it again before releasing it.
  • Keep critical sections short and avoid calling unknown code while holding the lock.
  • Dispose each successful Releaser exactly once. It is a value type, so copying it and disposing multiple copies releases the mutex more than once and corrupts ownership state.
  • Do not use the default value of Releaser; only use values returned by successful acquisition.
  • A cancellation token controls acquisition only, not work performed after acquisition.

API

Member Behavior
Lock() Acquires asynchronously without cancellation registration.
Lock(CancellationToken) Acquires asynchronously with cancellable waiting.
LockSync(CancellationToken) Blocks until acquired or cancelled.
TryLock(out Releaser) Attempts immediate acquisition.
Dispose() Rejects waiters without waiting for the holder.
DisposeAsync() Rejects waiters and waits for the holder to exit.
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (8)

Showing the top 5 NuGet packages that depend on Soenneker.Asyncs.Locks:

Package Downloads
Soenneker.Utils.AsyncSingleton

An externally initializing singleton that uses double-check asynchronous locking, with optional async and sync disposal

Soenneker.Dictionaries.SingletonKeys

An externally initializing singleton dictionary that uses double-check asynchronous locking, with optional async and sync disposal

Soenneker.Blazor.Utils.Session

A Blazor utility for access-token caching and optional idle-timeout navigation

Soenneker.Asyncs.Initializers

A lightweight, async-safe, allocation-free one-time initialization gate. Ensures a given asynchronous initialization routine runs exactly once, even under concurrent callers, with support for cancellation, safe publication, and disposal.

Soenneker.Utils.RateLimiting.Executor

A thread-safe utility designed to manage the rate at which tasks are executed, ensuring they are not run more frequently than a specified interval.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.0.78 11,689 9/4/2026
4.0.77 7,859 9/3/2026
4.0.76 10,862 9/3/2026
4.0.75 60 9/3/2026
4.0.74 44,493 8/31/2026
4.0.73 37,099 8/30/2026
4.0.72 158 8/30/2026
4.0.71 11,251 8/30/2026
4.0.70 22,386 8/29/2026
4.0.68 268,392 8/8/2026
4.0.67 33,706 8/8/2026
4.0.66 102,440 7/28/2026
4.0.65 32,682 7/28/2026
4.0.64 138 7/28/2026
4.0.63 19,413 7/27/2026
4.0.62 113,038 7/19/2026
4.0.61 33,539 7/18/2026
4.0.60 46,832 7/16/2026
4.0.59 112 7/16/2026
4.0.58 28,423 7/16/2026
Loading failed

Updated NuGet packages