Good afternoon. I read about async and await (John Skeet) and I can’t understand the moment about the synchronization context and the execution context. Can someone explain or direct where to read about this in a simpler language. I correctly understand that async-await does not create a new the flow, and works in the current? And how does ConfigureAwait help?

    1 answer 1

    No, no, async / await does not work with threads at all.

    See, in fact, an async function is not a function at all in the usual sense of the word. From the outside, it looks like a function that returns Task . But if we look from the inside, then coming out of the return value does not mean that the function code reached the return or the end of the function body! A normally incomplete Task returned from the async function.

    Wait, what is this beast - Task ? This is an abstraction, which is a task that will complete at some point. * For an async function, the task is to perform the function itself, and this task itself will be completed at the moment when the function reaches the return .

    So, let us have async-function F :

     async Task F() { var seconds = new Random().Next(10); await Task.Delay(TimeSpan.FromSeconds(seconds)); } 

    When we write Task t = F(); , then the assignment to the variable t occurs not when the function runs to the end, but almost immediately **. And what corresponds to the end of the Task ? Very simple, but this is an important point.

    When you write await t; (and you can write it only in another, external async method!), then this line will be executed when our Task completed. And while waiting for the completion of t , our external task will not block the flow, waiting for the end of the internal task t . For the waiting time, our external task will not be carried out anywhere , and will exist only in the form of an object. Upon completion of the assignment t our external assignment will “reappear” again and continue execution, as if nothing had happened.

    And where is the multithreading, you ask. Well, yes, she has practically nothing to do with it. Internal task t can be performed in the same, in another, in several or even in no thread, for await it does not matter, await just waits for the end of task t .

    Just earlier, before the appearance of async / await in order for the function to not block the entire application during the wait time, it was necessary to unload it into another thread. But now it is not necessary, async functions while waiting for await do not block anything, and the same functionality can be achieved without multithreading.


    Two more words about ConfigureAwait . When the async function resumes execution after await , in which thread does it do it? Well, actually, this is not necessarily the source thread in which she started her await (this thread can already be completed!). But there is an important special case: if we have a graphical application, and await started in the UI thread, then it will also continue in it (the execution context is responsible for this). This is usually important, since access to UI elements is possible only in the UI stream. There are also other situations where you would like to return after await to the same thread *** in which you started await ,

    So, if you don’t need it, if you don’t care where the continuation of the task will take place, then you can use ConfigureAwait(false) to inform the framework about this and make it a little easier for him. That's all. He can take advantage of this, and throw the tail of the task to the thread pool. Or maybe not take advantage of his right.


    Additional reading on the topic:


    * Well, or never will end. For example, inside an async function, you can easily arrange an infinite loop.

    ** When exactly, you can see in this more detailed description.

    *** Or a group of threads. Or even SynchronizationContext .

    • Thank. Very helpful. Much has become clear. The only thing not fully understood about the context of execution and synchronization is Zaur Yakubov
    • @ZaurYakubov: See, the synchronization context is such a thing that can bring the execution after await somewhere (for example, to a UI thread). Normally, the synchronization context is null (and after await code continues to run on the thread pool), but frameworks like WPF or WinForms set their synchronization context, which leads to the UI thread. - VladD
    • It turns out that the asynchronous method captures the execution context, then the calling code does not have access to the controls. What can the calling code do? In general, when is it reasonable to use async-await? When we download a file or something else? - Zaur Yakubov
    • @ZaurYakubov: Asynchronous method does not capture the execution context (and what does this mean?). This await looks to see if there is an execution context, and if there is, then at the end of the wait it uses it to resume execution in it. (And if not, it resumes execution on the thread pool.) // Async / await is always sensible to use (except when it is impossible to manage manual thread management). This is the preferred method for organizing asynchrony. - VladD
    • one
      @PetSerAl: Ah, confused with the sync context. Ugh, ooo you :) - VladD