using System.Collections.Generic;
using System.Threading.Tasks;
public static void Main()
const int maximumConcurrency = 2;
const int maximumParallelism = 1;
Dictionary<string, (Task, CancellationTokenSource)> commandos = new();
SemaphoreSlim semaphore = new(maximumConcurrency, maximumConcurrency);
TaskScheduler scheduler = new ConcurrentExclusiveSchedulerPair(
TaskScheduler.Default, maximumParallelism).ConcurrentScheduler;
Print($"Removing all commandos");
try { DisposeAllCommandos().GetAwaiter().GetResult(); } catch (OperationCanceledException) { }
void StartCommando(string commando)
Task existingTask = null;
CancellationTokenSource existingCts = null;
if (commandos.TryGetValue(commando, out var entry))
(existingTask, existingCts) = entry;
CancellationTokenSource cts = new();
CancellationToken token = cts.Token;
Task task = Task.Factory.StartNew(async () =>
if (existingTask is not null) try { await existingTask; } catch { }
await semaphore.WaitAsync(token);
await MyFunction(commando, token);
finally { semaphore.Release(); }
}, token, TaskCreationOptions.DenyChildAttach, scheduler).Unwrap();
commandos[commando] = (task, cts);
void StopCommando(string commando)
if (commandos.TryGetValue(commando, out var entry))
(_, CancellationTokenSource cts) = entry;
Task DisposeAllCommandos()
List<Task> tasks = new(commandos.Count);
foreach (var (commando, entry) in commandos)
(Task task, CancellationTokenSource cts) = entry;
commandos.Remove(commando);
return Task.WhenAll(tasks);
async Task MyFunction(string commando, CancellationToken token)
Print($"Starting {commando} iteration");
await Task.Delay(200, token);
Print($"Finishing {commando} iteration");
private static void Print(object value)
Console.WriteLine($@"{DateTime.Now:HH:mm:ss.fff} [{Thread.CurrentThread
.ManagedThreadId}] > {value}");