Saltar al contenido principal

ValkarnSemaphore

ValkarnSemaphore es un semáforo asíncrono de asignación cero construido sobre ValkarnTask. Funciona como SemaphoreSlim pero se integra de forma nativa con el pipeline de Valkarn Tasks — sin asignaciones de Task, sin presión sobre el GC en estado estable.

namespace UnaPartidaMas.Valkarn.Tasks

Ruta rápida (sin contención): devuelve ValkarnTask.CompletedTask inmediatamente — asignación cero. Ruta lenta (con contención): los nodos de espera se toman prestados de un pool acotado y se devuelven al completarse — GC cero en estado estable.


Constructor

public ValkarnSemaphore(int initialCount, int maxCount)
ParámetroDescripción
initialCountNúmero de ranuras disponibles de inmediato. Debe estar en [0, maxCount].
maxCountLímite superior para el recuento de ranuras. Debe ser > 0.

Propiedades

public int CurrentCount { get; } // Ranuras disponibles ahora mismo (thread-safe)

Métodos

WaitAsync

public ValkarnTask WaitAsync(CancellationToken ct = default)

Adquiere una ranura de forma asíncrona. Devuelve un ValkarnTask completado inmediatamente si hay una ranura disponible (asignación cero). Suspende al llamante hasta que se llame a Release() si no hay ranuras disponibles. Los waiters se atienden en orden FIFO.

Lanza OperationCanceledException si ct se activa mientras se espera.

Wait

public void Wait(CancellationToken ct = default)

Variante síncrona. Bloquea el hilo llamante si no hay ninguna ranura disponible. Úsela solo cuando no pueda usar await (p. ej. puntos de entrada no asíncronos).

Release

public void Release(int releaseCount = 1)

Devuelve releaseCount ranuras. Los waiters pendientes se completan en orden FIFO, fuera del lock interno — las continuaciones nunca se ejecutan con el lock tomado.

Lanza InvalidOperationException si la liberación excedería maxCount.

Dispose

public void Dispose()

Cancela todos los waiters pendientes y libera recursos. Es idempotente.


Uso

Exclusión mutua (1 ranura)

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();
}
}

Concurrencia acotada (N ranuras)

// Permite hasta 4 solicitudes de red concurrentes.
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();
}
}

Compuerta productor / consumidor

// Comienza con 0 ranuras — el consumidor espera hasta que el productor señalice.
var gate = new ValkarnSemaphore(initialCount: 0, maxCount: 1);

async ValkarnTask ProducerAsync(CancellationToken ct)
{
await ProduceDataAsync(ct);
gate.Release(); // señalizar al consumidor
}

async ValkarnTask ConsumerAsync(CancellationToken ct)
{
await gate.WaitAsync(ct); // esperar la señal
await ConsumeDataAsync(ct);
}

Seguridad de hilos

Todas las operaciones son thread-safe. Release() puede llamarse desde cualquier hilo, incluidos los hilos en segundo plano. Las completaciones se despachan fuera del lock interno — una continuación que reingresa al semáforo no producirá un deadlock.


Comparación con SemaphoreSlim

ValkarnSemaphoreSemaphoreSlim
Asignación en ruta rápidaCeroCero
Asignación en ruta lentaNodo en pool, GC ceroAsignación de Task
Se integra con ValkarnTaskRequiere .AsValkarnTask()
IDisposable
Orden FIFO
Cancelación