Table of Contents

AsyncSemaphore Class

Definition

Namespace
Bodu.Threading
Assembly
Bodu.Core.dll
Package
Bodu.Core 1.0.1
Source
AsyncSemaphore.Releaser.cs

Provides a lightweight asynchronous counting semaphore that admits a bounded number of concurrent holders and releases waiters in strict first-in, first-out order.

public sealed class AsyncSemaphore
Inheritance
AsyncSemaphore
Inherited Members
Extension Methods

Examples

private readonly AsyncSemaphore _throttle = new(initialCount: 4);

public async Task DownloadAsync(Uri uri)
{
    using (await _throttle.LockAsync())
    {
        // At most four downloads run concurrently.
        await HttpGetAsync(uri);
    }
}

Remarks

AsyncSemaphore is a queue-based asynchronous analogue of SemaphoreSlim. Callers await WaitAsync() to consume a permit and call Release() to return one. The convenience method LockAsync() pairs a wait with a disposable AsyncSemaphore.Releaser so a permit can be scoped with a using statement.

Unlike SemaphoreSlim, waiters are released in strict FIFO order: the longest-waiting caller is always satisfied first. Each waiter is represented by a TaskCompletionSource<TResult> created with RunContinuationsAsynchronously, so a thread calling Release() is never hijacked to run a waiter's continuation inline. The type owns no operating-system handle and therefore does not implement IDisposable.

Cancellation follows the package-wide rule: an available permit is taken even when the supplied token is already canceled. A token only cancels a wait that must queue.

Release(int) first hands permits directly to queued waiters in FIFO order and only stores the remainder as available permits. Permits transferred to waiters never appear in CurrentCount, and the configured maximum applies only to the stored available count after queued waiters have been satisfied. The bound is validated before any permit is granted, so a release that would exceed the maximum throws InvalidOperationException without granting any waiter or changing CurrentCount.

Constructors

AsyncSemaphore(int)

Initializes a new instance of the AsyncSemaphore class with the specified number of permits and no upper bound on the permit count.

public AsyncSemaphore(int initialCount)

Parameters

initialCount int

The initial number of permits available.

Exceptions

ArgumentOutOfRangeException

initialCount is negative.

AsyncSemaphore(int, int)

Initializes a new instance of the AsyncSemaphore class with the specified number of permits and maximum permit count.

public AsyncSemaphore(int initialCount, int maxCount)

Parameters

initialCount int

The initial number of permits available.

maxCount int

The maximum number of permits the semaphore can hold.

Exceptions

ArgumentOutOfRangeException

initialCount is negative, maxCount is less than one, or initialCount is greater than maxCount.

Properties

CurrentCount

Gets the number of permits currently available.

public int CurrentCount { get; }

Property Value

int

The number of permits that can be taken without waiting.

Methods

LockAsync()

Asynchronously takes a permit and returns a disposable releaser that returns it when disposed.

public ValueTask<AsyncSemaphore.Releaser> LockAsync()

Returns

ValueTask<AsyncSemaphore.Releaser>

A ValueTask<TResult> yielding a AsyncSemaphore.Releaser whose disposal returns the permit.

LockAsync(CancellationToken)

Asynchronously takes a permit and returns a disposable releaser that returns it when disposed, observing a cancellation request while waiting.

public ValueTask<AsyncSemaphore.Releaser> LockAsync(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken

A token used to cancel the pending wait.

Returns

ValueTask<AsyncSemaphore.Releaser>

A ValueTask<TResult> yielding a AsyncSemaphore.Releaser whose disposal returns the permit.

Exceptions

OperationCanceledException

cancellationToken was canceled before a permit was taken.

Release()

Returns a single permit to the semaphore, releasing the next waiter if one is queued.

public void Release()

Exceptions

InvalidOperationException

Releasing would raise the permit count above the configured maximum.

Release(int)

Returns the specified number of permits to the semaphore, releasing queued waiters in FIFO order.

public void Release(int releaseCount)

Parameters

releaseCount int

The number of permits to return.

Exceptions

ArgumentOutOfRangeException

releaseCount is less than one.

InvalidOperationException

Releasing would raise the permit count above the configured maximum. The release is atomic: when this exception is thrown, no waiter has been granted and CurrentCount is unchanged.

WaitAsync()

Asynchronously waits to take a permit.

public ValueTask WaitAsync()

Returns

ValueTask

A ValueTask that completes when a permit has been taken.

Examples

// Manual permit management; pair every WaitAsync with a Release in a finally block.
await _throttle.WaitAsync();
try
{
    await DoWorkAsync();
}
finally
{
    _throttle.Release();
}

Remarks

The returned ValueTask must be awaited exactly once.

WaitAsync(CancellationToken)

Asynchronously waits to take a permit, observing a cancellation request while waiting.

public ValueTask WaitAsync(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken

A token used to cancel the pending wait.

Returns

ValueTask

A ValueTask that completes when a permit has been taken.

Remarks

The returned ValueTask must be awaited exactly once. An available permit is taken even when cancellationToken is already canceled; the token only cancels a wait that must queue, and cancellation removes only the calling waiter from the queue.

Exceptions

OperationCanceledException

cancellationToken was canceled before a permit was taken.

Applies to

ProductVersions
.NET8, 10