A high-performance background Task/ValueTask queue
$ dotnet add package Soenneker.Utils.BackgroundQueue
Soenneker.Utils.BackgroundQueueBackgroundQueue provides a fast, controlled way to execute background work in .NET applications.
It prevents overload by queueing and processing work asynchronously with configurable limits and built-in tracking.
Task and ValueTaskdotnet add package Soenneker.Utils.BackgroundQueue
Register the queue:
void ConfigureServices(IServiceCollection services)
{
services.AddBackgroundQueueAsSingleton();
}
await serviceProvider.WarmupAndStartBackgroundQueue(cancellationToken);
Synchronous start:
serviceProvider.WarmupAndStartBackgroundQueueSync(cancellationToken);
await serviceProvider.StopBackgroundQueue(cancellationToken);
Synchronous stop:
serviceProvider.StopBackgroundQueueSync(cancellationToken);
{
"Background": {
"QueueLength": 5000,
"LockCounts": false,
"Log": false
}
}
QueueLength – Maximum number of queued itemsLockCounts – Enables thread-safe tracking of running workLog – Enables debug loggingInject IBackgroundQueue:
IBackgroundQueue _queue;
void MyClass(IBackgroundQueue queue)
{
_queue = queue;
}
ValueTaskawait _queue.QueueValueTask(_ => someValueTask(), cancellationToken);
Taskawait _queue.QueueTask(_ => someTask(), cancellationToken);
Avoid capturing variables in lambdas when queueing work. Captured lambdas allocate and can impact performance under load.
await _queue.QueueTask(ct => DoWorkAsync(id, ct));
If id is a local variable, this creates a closure.
Use the stateful overloads with static lambdas.
await _queue.QueueValueTask(
myService,
static (svc, ct) => svc.ProcessAsync(ct),
ct);
await _queue.QueueTask(
(logger, id),
static (s, ct) => s.logger.RunAsync(s.id, ct),
ct);
Why this is better:
The non-stateful overloads remain available for convenience, but stateful queueing is recommended for hot paths.
await queue.WaitUntilEmpty(cancellationToken);
Check if work is still processing:
bool isProcessing = await queueInformationUtil.IsProcessing(cancellationToken);
Get current counts:
var (taskCount, valueTaskCount) =
await queueInformationUtil.GetCountsOfProcessing(cancellationToken);