I'm actually studying async/wait and trying to see for myself the benefit of await Task.WhenAll versus Task.WaitAll in CPU bound operations. As everyone write that Task.WaitAll provides a blocking wait while await Task.WhenAll provides a non-blocking wait.
I created an example in which I wanted to replace Task.WaitAll with an await Task.WhenAll and see with my own eyes that there was one more free thread. But I see that even Task.WaitAll does not block the thread. And my question is related to this. In the case of Task.WaitAll, I see that in the same thread in which Task.WaitAll is executed, another task is being executed. But if I include Thread.Sleep or while (true) instead of Task.WaitAll, then the behavior of the program becomes as expected.
I thought that the Main method will create task MyTask (-1 worker thread), which will create 16 tasks conditionally B1-B16 (-15 worker threads since 1 worker thread is busy with task MyTask, and there are 16 worker threads in total), task MyTask will have a blocking wait Task.WaitAll and I will see 15 out of 16 running tasks. But I see all 16 running tasks and one of them is running on the same thread that task MyTask is running on.
Question. Why does Task.WaitAll not block the thread in which it is executed in this example, unlike Thread.Sleep or while (true)? Can someone explain step by step how the code of two tasks in thread 4 works in case of using Task.WaitAll? Why is the thread in which task MyTask runs also used by task conditionally B16?
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { static void Main(string[] args) { Console.WriteLine($"Main Thread: {Thread.CurrentThread.ManagedThreadId}"); int ProcessorCount = Environment.ProcessorCount; ThreadPool.SetMaxThreads(ProcessorCount, ProcessorCount); int Counter = 0; List<Task> MyListForTask = new List<Task>(); void MyMethod() { lock (MyListForTask) { Counter++; Console.WriteLine($"Counter: {Counter} Thread: {Thread.CurrentThread.ManagedThreadId}"); } //Thread.Sleep(int.MaxValue); while (true) { }; } Task MyTask = Task.Run(() => { Console.WriteLine($"MyTask Thread: {Thread.CurrentThread.ManagedThreadId}\n"); for (int i = 0; i < ProcessorCount; i++) { MyListForTask.Add(Task.Run(MyMethod)); } //Thread.Sleep(int.MaxValue); //while (true) { }; Task.WaitAll(MyListForTask.ToArray()); }); MyTask.Wait(); } } } 



MyTask.Wait()is "stealing" work from one of the other tasks in the list. You might want to test this hypothesis by replacing theTask.Runwith theTask.Factory.StartNew, and passing theTaskCreationOptions.PreferFairnessoption.Task.WaitAllorThread.SleepinMyTask? Sounds weird... I replacedMyTask.Wait()withThread.Sleep(int.MaxValue), nothing changed.Thread.Sleepyou make all observations invalid. Tasks aren't threads, they're essentially promises/job descriptions. Tasks run on reusable threadpool threads. CallingThread.Sleepnot only blocks those threads, it removes them from the OS's scheduling, forcing the runtime to create new ones. If you want to emulate waiting useThread.SpinWaitat least.Thread.Sleepinstead ofTask.WaitAll16 tasks are started and one is scheduled, in the case ofTask.WaitAllinstead ofThread.Sleepall 17 tasks are started and blocked. Does this refer to an invalid observation? I look not only at the console. One task did not have enough flow. Can you explain step by step how the code of the two tasks in thread 4 works in case of using Task.WaitAll?