Pular para o conteúdo principal

ValkarnSemaphore

ValkarnSemaphore é um semáforo assíncrono de alocação zero construído sobre ValkarnTask. Funciona como SemaphoreSlim, mas se integra nativamente ao pipeline do Valkarn Tasks — sem alocações de Task, sem pressão no GC em estado estável.

namespace UnaPartidaMas.Valkarn.Tasks

Caminho rápido (sem contenção): retorna ValkarnTask.CompletedTask imediatamente — alocação zero. Caminho lento (com contenção): os nós de espera são alugados de um pool limitado e devolvidos ao concluir — GC zero em estado estável.


Construtor

public ValkarnSemaphore(int initialCount, int maxCount)
ParâmetroDescrição
initialCountNúmero de slots disponíveis imediatamente. Deve estar em [0, maxCount].
maxCountLimite superior para a contagem de slots. Deve ser > 0.

Propriedades

public int CurrentCount { get; } // Slots disponíveis agora (thread-safe)

Métodos

WaitAsync

public ValkarnTask WaitAsync(CancellationToken ct = default)

Adquire um slot de forma assíncrona. Retorna um ValkarnTask concluído imediatamente se um slot estiver disponível (alocação zero). Suspende o chamador até que Release() seja chamado se não houver slot disponível. Os waiters são atendidos em ordem FIFO.

Lança OperationCanceledException se ct for acionado enquanto aguarda.

Wait

public void Wait(CancellationToken ct = default)

Variante síncrona. Bloqueia o thread chamador se não houver slot disponível. Use somente quando não for possível usar await (p. ex. pontos de entrada não assíncronos).

Release

public void Release(int releaseCount = 1)

Devolve releaseCount slots. Os waiters pendentes são concluídos em ordem FIFO, fora do lock interno — as continuações nunca são executadas com o lock mantido.

Lança InvalidOperationException se a liberação excedesse maxCount.

Dispose

public void Dispose()

Cancela todos os waiters pendentes e libera recursos. Idempotente.


Uso

Exclusão mútua (1 slot)

var sem = new ValkarnSemaphore(initialCount: 1, maxCount: 1);

async ValkarnTask WriteFileAsync(string path, string content, CancellationToken ct)
{
await sem.WaitAsync(ct);
try
{
await File.WriteAllTextAsync(path, content, ct);
}
finally
{
sem.Release();
}
}

Concorrência limitada (N slots)

// Permite até 4 requisições de rede concorrentes.
var sem = new ValkarnSemaphore(initialCount: 4, maxCount: 4);

async ValkarnTask FetchAsync(string url, CancellationToken ct)
{
await sem.WaitAsync(ct);
try
{
await DownloadAsync(url, ct);
}
finally
{
sem.Release();
}
}

Portão produtor / consumidor

// Começa com 0 slots — o consumidor aguarda até que o produtor sinalize.
var gate = new ValkarnSemaphore(initialCount: 0, maxCount: 1);

async ValkarnTask ProducerAsync(CancellationToken ct)
{
await ProduceDataAsync(ct);
gate.Release(); // sinalizar o consumidor
}

async ValkarnTask ConsumerAsync(CancellationToken ct)
{
await gate.WaitAsync(ct); // aguardar o sinal
await ConsumeDataAsync(ct);
}

Segurança de threads

Todas as operações são thread-safe. Release() pode ser chamado de qualquer thread, incluindo threads em segundo plano. As conclusões são despachadas fora do lock interno — uma continuação que reentra no semáforo não causará deadlock.


Comparação com SemaphoreSlim

ValkarnSemaphoreSemaphoreSlim
Alocação no caminho rápidoZeroZero
Alocação no caminho lentoNó em pool, GC zeroAlocação de Task
Integração com ValkarnTaskSimRequer .AsValkarnTask()
IDisposableSimSim
Ordenação FIFOSimSim
CancelamentoSimSim