NExtensions.Async 1.1.0

dotnet add package NExtensions.Async --version 1.1.0
                    
NuGet\Install-Package NExtensions.Async -Version 1.1.0
                    
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="NExtensions.Async" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NExtensions.Async" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="NExtensions.Async" />
                    
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 NExtensions.Async --version 1.1.0
                    
#r "nuget: NExtensions.Async, 1.1.0"
                    
#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 NExtensions.Async@1.1.0
                    
#: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=NExtensions.Async&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=NExtensions.Async&version=1.1.0
                    
Install as a Cake Tool

NExtensions.Async

NuGet Version License: MIT .NET

High-performance async synchronization primitives for modern .NET applications. This library provides efficient, allocation-friendly implementations of essential async coordination types: AsyncReaderWriterLock, AsyncLock, and AsyncLazy. Inspired from the awesome library of Stephen Cleary.

✨ Features

  • 🔒 AsyncLock: Asynchronous mutual-exclusion lock
  • ⚡ AsyncReaderWriterLock: Multiple concurrent readers or single exclusive writer
  • ⏳ AsyncLazy<T>: Thread-safe asynchronous lazy initialization with multiple safety modes
  • Cancellation Support: Thorough CancellationToken support across all primitives
  • Zero dependencies: No external dependencies
  • Modern .NET: Supports .NET 6.0, 7.0, 8.0, and 9.0 with nullable reference types

Installation

dotnet add package NExtensions.Async

🚀 Quick Start

AsyncLock

An asynchronous mutual-exclusion lock that allows only one thread to enter the critical section at a time.

var asyncLock = new AsyncLock();

// Basic usage
using (await asyncLock.EnterScopeAsync())
{
    // Only one thread at a time
    await ProcessSharedResourceAsync();
}

// With cancellation
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try
{
    using (await asyncLock.EnterScopeAsync(cts.Token))
    {
        await LongRunningOperationAsync();
    }
}
catch (OperationCanceledException)
{
    // Handle timeout
}

// Allow synchronous continuations for better performance (use with care)
var fastLock = new AsyncLock(allowSynchronousContinuations: true);

AsyncReaderWriterLock

A reader-writer lock allowing multiple concurrent readers or a single exclusive writer.

var rwLock = new AsyncReaderWriterLock();

// Multiple readers can access simultaneously
using (await rwLock.EnterReaderScopeAsync())
{
    var data = await ReadOnlyOperationAsync();
    // Other readers can run concurrently
}

// Writers get exclusive access
using (await rwLock.EnterWriterScopeAsync())
{
    await ModifyDataAsync();
    // No other readers or writers allowed
}

// Configure synchronous continuations independently
var customRwLock = new AsyncReaderWriterLock(
    allowSynchronousReaderContinuations: true,
    allowSynchronousWriterContinuations: false
);

AsyncLazy<T>

Thread-safe asynchronous lazy initialization with configurable safety modes.

// Basic lazy initialization
var lazy = new AsyncLazy<DatabaseConnection>(
    async () => await ConnectToDatabaseAsync()
);

DatabaseConnection connection = await lazy;
DatabaseConnection sameConnection = await lazy; // Same instance, no re-initialization

// Consume with cancellation support
var lazyWithCancellation = new AsyncLazy<string>(
    async (ct) => await DownloadDataAsync(ct)
);
string data = await lazyWithCancellation.GetAsync(cancellationToken);

// Configure safety modes
var retryableLazy = new AsyncLazy<string>(
    async () => await UnreliableOperationAsync(),
    LazyAsyncSafetyMode.ExecutionAndPublicationWithRetry
);
AsyncLazy Safety Modes
// No thread safety, no retry (fastest)
var noneMode = new AsyncLazy<string>(factory, LazyAsyncSafetyMode.None);

// No thread safety, but retry on failure
var noneWithRetry = new AsyncLazy<string>(factory, LazyAsyncSafetyMode.NoneWithRetry);

// Thread-safe publication, retry on failure 
var publicationOnly = new AsyncLazy<string>(factory, LazyAsyncSafetyMode.PublicationOnly);

// Thread-safe execution and publication, cache exceptions (default-like behavior)
var executionAndPublication = new AsyncLazy<string>(factory, LazyAsyncSafetyMode.ExecutionAndPublication);

// Thread-safe execution and publication with retry (safest)
var safestMode = new AsyncLazy<string>(factory, LazyAsyncSafetyMode.ExecutionAndPublicationWithRetry);

🔧 Synchronous Continuations

Both AsyncReaderWriterLock, AsyncLock support synchronous continuations for improved performance. This feature is disabled by default, as asynchronous continuations are considered the safer default. Enable it only with caution and after prior benchmarking.

// Can be faster, but be careful of reentrancy and stack diving
var fastLock = new AsyncLock(allowSynchronousContinuations: true);

// Fine-tune reader vs writer continuation behavior
var customRwLock = new AsyncReaderWriterLock(
    allowSynchronousReaderContinuations: true,  // Safe for read-only operations
    allowSynchronousWriterContinuations: false  // Writers might need async context
);

🎯 Notes

  • Always dispose releasers: Use using statements to ensure locks are released
  • Handle cancellation: Provide appropriate CancellationToken values for timeout scenarios
  • Choose safety modes wisely: Balance performance vs. safety based on your requirements
  • Be cautious with sync continuations: Only enable when you understand the implications
  • Debugging: All types include comprehensive debugging support

📊 Benchmarks

BenchmarkDotNet v0.15.1, Windows 11 (10.0.26100.4770/24H2/2024Update/HudsonValley)
13th Gen Intel Core i9-13900KF 3.00GHz, 1 CPU, 32 logical and 24 physical cores
.NET SDK 8.0.411
  [Host]     : .NET 8.0.18 (8.0.1825.31117), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.18 (8.0.1825.31117), X64 RyuJIT AVX2

AsyncLock

Method Hits Parallelism Wait Mean Error StdDev Completed Work Items Lock Contentions Gen0 Gen1 Allocated
SemaphoreSlim 150000 1 yield 57.31 ms 0.256 ms 0.239 ms 151662.2222 - 888.8889 - 16.02 MB
AsyncExLock 150000 1 yield 61.42 ms 0.445 ms 0.416 ms 150544.0000 - 3444.4444 - 61.8 MB
AsyncLock 150000 1 yield 57.15 ms 0.163 ms 0.152 ms 151519.3333 - 888.8889 - 16.02 MB
SemaphoreSlim 150000 20 yield 3,502.90 ms 49.994 ms 41.747 ms 6041664.0000 48.0000 77000.0000 1000.0000 1373.29 MB
AsyncExLock 150000 20 yield 3,173.30 ms 35.980 ms 31.896 ms 9101322.0000 81.0000 135000.0000 7000.0000 2426.15 MB
AsyncLock 150000 20 yield 2,507.91 ms 34.059 ms 31.859 ms 6032875.0000 6.0000 23000.0000 - 412 MB

AsyncReaderWriterLock

Method RW Hits Wait Continuation Mean Error StdDev Median Gen0 Completed Work Items Lock Contentions Allocated
AsyncExReaderWriterLock 1/10 150000 yield AsyncReadWrite 245.6 ms 4.87 ms 4.78 ms 244.5 ms 11000.0000 604780.0000 9.0000 208.29 MB
AsyncReaderWriterLock 1/10 150000 yield AsyncReadWrite 197.7 ms 3.83 ms 4.98 ms 196.8 ms 2000.0000 453012.0000 1.0000 36.63 MB
AsyncExReaderWriterLock 10/1 150000 yield AsyncReadWrite 204.8 ms 4.03 ms 7.67 ms 205.4 ms 10000.0000 517546.0000 3434.0000 191.23 MB
AsyncReaderWriterLock 10/1 150000 yield AsyncReadWrite 173.2 ms 3.46 ms 9.47 ms 172.6 ms 2000.0000 453465.6667 387.3333 37.95 MB
AsyncExReaderWriterLock 10/10 150000 yield AsyncReadWrite 250.8 ms 5.01 ms 11.11 ms 252.5 ms 11000.0000 606837.0000 698.0000 208.3 MB
AsyncReaderWriterLock 10/10 150000 yield AsyncReadWrite 179.2 ms 3.54 ms 4.73 ms 179.2 ms 2000.0000 454672.0000 171.0000 36.63 MB
AsyncExReaderWriterLock 10/5 150000 yield AsyncReadWrite 250.5 ms 4.99 ms 8.47 ms 246.6 ms 11000.0000 605912.0000 171.0000 208.29 MB
AsyncReaderWriterLock 10/5 150000 yield AsyncReadWrite 179.9 ms 3.49 ms 3.88 ms 180.3 ms 2000.0000 454856.3333 187.6667 36.63 MB
AsyncExReaderWriterLock 5/10 150000 yield AsyncReadWrite 238.6 ms 4.30 ms 4.22 ms 237.7 ms 11000.0000 607103.0000 77.0000 208.29 MB
AsyncReaderWriterLock 5/10 150000 yield AsyncReadWrite 168.2 ms 3.26 ms 3.62 ms 168.5 ms 2000.0000 455618.6667 12.6667 36.63 MB

AsyncLazy

Method Parallelism Mean Error StdDev Ratio RatioSD Gen0 Completed Work Items Lock Contentions Allocated Alloc Ratio
Lazy_ExecutionAndPublication 1 0.0007 ms 0.0000 ms 0.0000 ms 1.00 0.00 0.0200 1.0189 0.0000 386 B 1.00
AsyncExLazy_ExecutionAndPublication 1 0.0011 ms 0.0000 ms 0.0000 ms 1.61 0.01 0.0362 2.0009 0.0000 696 B 1.80
AsyncLazy_ExecutionAndPublication 1 0.0008 ms 0.0000 ms 0.0000 ms 1.14 0.01 0.0372 1.0274 0.0000 708 B 1.83
Lazy_ExecutionAndPublication 10 0.0025 ms 0.0000 ms 0.0001 ms 1.00 0.03 0.0648 6.7474 0.0001 1254 B 1.00
AsyncExLazy_ExecutionAndPublication 10 0.0035 ms 0.0000 ms 0.0000 ms 1.40 0.03 0.1030 11.1279 0.0002 1968 B 1.57
AsyncLazy_ExecutionAndPublication 10 0.0025 ms 0.0000 ms 0.0001 ms 1.01 0.03 0.0839 6.5738 0.0000 1594 B 1.27
Lazy_ExecutionAndPublication 200 0.0160 ms 0.0001 ms 0.0001 ms 1.00 0.01 0.0610 12.3357 0.0050 1341 B 1.00
AsyncExLazy_ExecutionAndPublication 200 0.0431 ms 0.0009 ms 0.0015 ms 2.68 0.09 0.0610 18.9797 1.7889 2231 B 1.66
AsyncLazy_ExecutionAndPublication 200 0.0160 ms 0.0001 ms 0.0001 ms 0.99 0.01 0.0916 12.8473 0.0040 1784 B 1.33

📄 License

This project is licensed under the MIT License.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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.
  • net6.0

    • No dependencies.
  • net7.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 123 8/10/2025
1.0.0 163 8/8/2025

Added .NET 9 support including System.Threading.Lock integration.
           Continued support for AsyncReaderWriterLock, AsyncLazy, and AsyncLock.
           Enhanced compatibility with the latest .NET runtime.