using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading.Channels;
using System.Threading.Tasks;
public static void Main()
Console.WriteLine("Hello World");
var log = (object msg) => Console.WriteLine(msg);
var workload = Enumerable
.Select(i => (Index: i, Delay: Random.Shared.Next(10, 50)))
await InstrumentedRun("Channel", async () => {
var channel = Channel.CreateUnbounded<int>();
async Task Run(ChannelWriter<int> writer, int id, int delay) {
await writer.WriteAsync(id);
async Task Receive(ChannelReader<int> reader) {
while (await reader.WaitToReadAsync()) {
if (reader.TryRead(out var id)) {
Console.WriteLine($"Completed {id}");
var receiveTask = Receive(channel.Reader);
var processingTasks = workload
.Select(e => Run(channel.Writer, e.Index, e.Delay));
.WhenAll(processingTasks)
.ContinueWith(_ => channel.Writer.Complete());
await InstrumentedRun("Parallel.For @ 4", () => {
Parallel.For(0, 100, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (index) => {
Thread.Sleep(workload[index].Delay);
return Task.CompletedTask;
await InstrumentedRun("Parallel.ForEachAsync @ 4", async () =>
await Parallel.ForEachAsync(workload, new ParallelOptions { MaxDegreeOfParallelism = 4 }, async (item, cancel) => {
await Task.Delay(item.Delay, cancel);
await InstrumentedRun("Parallel.ForEachAsync @ 40", async () =>
await Parallel.ForEachAsync(workload, new ParallelOptions { MaxDegreeOfParallelism = 40 }, async (item, cancel) => {
await Task.Delay(item.Delay, cancel);
await InstrumentedRun("Parallel.ForEachAsync (Default)", async () =>
await Parallel.ForEachAsync(workload, async (item, cancel) => {
await Task.Delay(item.Delay, cancel);
async Task InstrumentedRun(string name, Func<Task> test) {
var threadsAtStart = Process.GetCurrentProcess().Threads.Count;
var timer = new Stopwatch();
Console.WriteLine($"[{name}] = {timer.ElapsedMilliseconds}ms");
Console.WriteLine($" ⮑ {threadsAtStart} threads at start");
Console.WriteLine($" ⮑ {Process.GetCurrentProcess().Threads.Count} threads at end");