Checking if a thread is returning to a thread pool

How to check if a thread has been returned to a thread pool using the C # 2015 VS debugger?

What problem in my case is that it cannot be detected by debugging line by line.

async Task foo() { int y = 0; await Task.Delay(5); // (1) thread 2000 returns to thread pool here... while (y<5) y++; } async Task testAsync() { Task task = foo(); // (2) ... and here thread 2000 is back from the thread pool, to run the code below. I want // to confirm that it was in the thread pool in the meantime, using debugger. int i = 0; while (i < 100) { Console.WriteLine("Async 1 before: " + i++); } await task; } 

The first line of testAsync running in thread 2000 calls foo . When it encounters await Task.Delay(5) , thread 2000 returns to the thread pool (presumably I'm trying to confirm this), and the method waits for Task.Delay(5) . In the meantime, control returns to the caller, and the first testAsync also runs in thread 2000.

Thus, between two consecutive lines of code, a thread returns to the thread pool and returns from there. How can I confirm this with a debugger? Perhaps with a Threads debug window?

To clarify a bit more, what I'm asking is: foo runs on thread 2000. Two scenarios are possible:

  • When he accesses await Task.Delay(5) , thread 2000 returns to the thread pool for a very short time, and the control returns to the caller in line (2), which will be executed in thread 2000 taken from the pool thread. If so, you cannot easily detect this because Thread 2000 was in the thread pool during between two consecutive lines of code .

  • When it falls into await Task.Delay(5) , thread 2000 does not return to the thread pool, but immediately executes the code in testAsync , starting at line (2)

I want to check what really happens.

+6
source share
3 answers

There is a big mistake in your assumption:

When it reaches Task.Delay (5) expectations, thread 2000 returns to thread pool

Since you have not yet waited for foo() when thread 2000 accesses Task.Delay(5) , it simply creates a new Task and returns to testAsync() (to int i = 0; ). It goes into the while block, and only after that you wait for the Task . In this case, if Task is not completed yet, and, expecting the rest of the code to be expected, thread 2000 will return to the thread pool. Otherwise, if Task already completed, it will continue synchronously with foo() (at while (y<5) y++; ).

EDIT:

What if the main method is called testAsync?

When the synchronous method calls and waits for the async method, it should block the thread if the async method returns an incomplete task:

 void Main() { var task = foo(); task.Wait(); //Will block the thread if foo() is not completed. } 

Please note that in the above case, the thread does not return to the thread pool - it is completely suspended by the OS.

Maybe you can give an example of calling testAsync so that thread 2000 returns to the thread pool?

Assuming thread 2k is the main thread, it cannot return to the thread pool. But you can use Task.Run(()=> foo()) to run foo() in the thread pool, and since the calling thread is the main thread, another thread pool thread will raise this task. So the following code:

 static void Main(string[] args) { Console.WriteLine("main started on thread {0}", Thread.CurrentThread.ManagedThreadId); var testAsyncTask = Task.Run(() => testAsync()); testAsyncTask.Wait(); } static async Task testAsync() { Console.WriteLine("testAsync started on thread {0}", Thread.CurrentThread.ManagedThreadId); await Task.Delay(1000); Console.WriteLine("testAsync continued on thread {0}", Thread.CurrentThread.ManagedThreadId); } 

The following output was produced (on my PC):

 main started on thread 1 testAsync started on thread 3 testAsync continued on thread 4 Press any key to continue . . . 

Threads 3 and 4 came and returned to the thread pool.

+3
source

You can print Thread.CurrentThread.ManagedThreadId on the console. Note that the thread pool can reuse the same thread to continue on it, so there is no guarantee that it will be different:

 void Main() { TestAsync().Wait(); } public async Task FooAsync() { int y = 0; await Task.Delay(5); Console.WriteLine($"After awaiting in FooAsync: {Thread.CurrentThread.ManagedThreadId }"); while (y < 5) y++; } public async Task TestAsync() { Console.WriteLine($"Before awaiting in TestAsync: {Thread.CurrentThread.ManagedThreadId }"); Task task = foo(); int i = 0; while (i < 100) { var x = i++; } await task; Console.WriteLine($"After awaiting in TestAsync: {Thread.CurrentThread.ManagedThreadId }"); } 

Another thing you can check is ThreadPool.GetAvailableThreads to determine if another worker has been distributed for use:

 async Task FooAsync() { int y = 0; await Task.Delay(5); Console.WriteLine("Thread-Pool threads after first await:"); int avaliableWorkers; int avaliableIo; ThreadPool.GetAvailableThreads(out avaliableWorkers, out avaliableIo); Console.WriteLine($"Available Workers: { avaliableWorkers}, Available IO: { avaliableIo }"); while (y < 1000000000) y++; } async Task TestAsync() { int avaliableWorkers; int avaliableIo; ThreadPool.GetAvailableThreads(out avaliableWorkers, out avaliableIo); Console.WriteLine("Thread-Pool threads before first await:"); Console.WriteLine($"Available Workers: { avaliableWorkers}, Available IO: { avaliableIo }"); Console.WriteLine("-------------------------------------------------------------"); Task task = FooAsync(); int i = 0; while (i < 100) { var x = i++; } await task; } 

On my machine, this gives:

 Thread-Pool threads before first await: Available Workers: 1023, Available IO: 1000 ---------------------------------------------- Thread-Pool threads after first await: Available Workers: 1022, Available IO: 1000 
+3
source

I want to check what really happens.

It is not possible to "verify" this using the debugger, because the debugger is designed to simulate a logical (synchronous) stream - see the Walkthrough: Using the Debugger with Async Methods .

To understand what is happening (FYI is your case (2)), you need to find out how await works, starting with Asynchronous programming with Async and Await - What happens in the Async Method section , Flow control in Async programs and many other sources,

Take a look at this snippet:

 static void Main(string[] args) { Task.Run(() => { // Initial thread pool thread var t = testAsync(); t.Wait(); }); Console.ReadLine(); } 

If we make a lambda async and use await t; instead of t.Wait(); , this is the point at which the original thread will be returned to the thread pool. As I mentioned above, you cannot verify this with the debugger. But look at the code above and think logically - we block the initial thread, so if it wasn’t free, your testAsync and foo methods will not be able to resume. But it is, and this can be easily verified by setting breakpoints after await .

+1
source

All Articles