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