using System.Threading.Tasks;
using System.Collections.Generic;
public static async Task Main()
var p = new LimitedPar<int>(3);
var rnd = new Random(42);
List<Task> awaited = new(5);
for (var i = 0; i < 5; ++i)
var j = p.RunAsync(i, async n =>
var randomDelay = (int)rnd.NextInt64(1000, 3000);
Console.WriteLine($"Starting job #{n}, expected finish in {randomDelay}ms...");
await Task.Delay(randomDelay);
Console.WriteLine($"Finished job #{n}.");
await Task.WhenAll(awaited);
public static class LimitedPar
public const int DefaultParLevel = 3;
public sealed class LimitedPar<S>
public readonly int ParLevel;
private Queue<(Func<S, Task>, S)>? _queue;
private int _currentJobRunning;
public LimitedPar(int parLevel = LimitedPar.DefaultParLevel) =>
public async Task RunAsync(S state, Func<S, Task> job)
if (_currentJobRunning < ParLevel)
if (_queue?.TryDequeue(out var next) == true)
_queue.Enqueue((job, state));