How does the runtime know when to spawn a thread when using "wait"?

EDIT

I took John's comment and repeated it all. And indeed blocks the user interface thread. I must have messed up my initial test somehow. The string "OnResume exits" is written after SomeAsync completes . If the method is used to use await Task.WhenAll(t) , it will (as expected) not be blocked. Thanks for entering! At first I thought about deleting the question, because the original assumption was simply wrong, but I think the answers contain valuable information that should not be lost.

Original post:

Trying to understand the depth of internal asynchronous expectations. The example below is in an Android app using Xamarin. OnResume() is executed in the user interface thread.

  • SomeAsync() starts a new task (= it spawns a thread). Then it uses Task.WaitAll() to wait for the lock (we will not discuss it now if WhenAll() is the best option).
  • I see that the user interface is not blocked when Task.WaitAll() started. Therefore, SomeAsync() does not work in the user interface thread. This means that a new thread has been created.

How await "knows" that it should spawn a stream here - will it always be? If I change WaitAll() to WhenAll() , there will be no need for an extra thread as fast as I understand.

 // This runs on the UI thread. async override OnResume() { // What happens here? Not necessarily a new thread I suppose. But what else? Console.WriteLine ("OnResume is about to call an async method."); await SomeAsync(); // Here we are back on the current sync context, which is the UI thread. SomethingElse(); Console.WriteLine ("OnResume exits"); } Task<int> SomeAsync() { var t = Task.Factory.StartNew (() => { Console.WriteLine("Working really hard!"); Thread.Sleep(10000); Console.WriteLine("Done working."); }); Task.WhenAll (t); return Task.FromResult (42); } 
+6
source share
2 answers

Simple: it never spawns a thread for await . If the expected is already completed, it simply continues to work; if the expected is not completed, it simply tells the expected instance to add a continuation (via a rather complex state machine). When the thing being completed is completed, it will cause the continuation (usually via sync-context, if one is otherwise synchronous in the thread that marks the work as completed). But! The synthesis context could theoretically be the one that selects what needs to be superimposed on the thread pool (most user interface synchronization contexts, however, push objects into the user interface thread).

+6
source

I think you will find this topic interesting: How is the C # 5.0 asynchronous wait function different from TPL?

In short, await does not run threads.

What he does is simply β€œsplit” the code in the place where the line is located, where β€œexpect”, and all that line is added as a continuation to the Task .

Pay attention to the task. Please note that you have Factory.StartNew . Thus, in your code, it is Factory, which actually launches the task - and includes placement in some thread, whether it is a user interface or pool or any other task scheduler. This means that a "Task" is usually assigned to some scheduler when fulfilling the wait.

Of course, it does not need to be assigned or started at all. The only important thing is that you need to have a task, in fact.

If the Task is not running - the wait does not bother. He simply joins the sequel, and you will solve this problem later. And assign it to the right scheduler.

+1
source

All Articles