Asynchronous Doesn't Assume Parallel
Asynchronous only means concurrency. In fact, even using explicit threads does not guarantee that they will be executed at the same time (for example, when thread merges for the same core or, more often, when there is only one core in the machine).
Therefore, you should not expect that the asynchronous operation will be performed simultaneously with something else. Asynchronous means that this will happen, after all, at another time (a (Greek) = without, syn (Greek) = together, khronos (Greek) = time. => Asynchronous = does not happen at the same time).
Note. The idea of asynchrony is that when you call you don’t care when the code really runs. This allows the system to use parallelism, if possible, to perform the operation. It can even start immediately. This can happen even in the same topic ... more on this later.
When you await do an asynchronous operation, you create concurrency (com (latin) = together, currere (latin) = run. => "Concurrent" = to run together). This is because you are asking the asynchronous operation to complete before moving on. We can say that the execution is converging. This is similar to the concept of thread pooling.
If asynchronous cannot be parallel
When you use async / wait, there is no guarantee that the method you call when you expect FooAsync () will actually execute asynchronously. An internal implementation can be returned using a fully synchronous path.
This can happen in three ways:
You can use await for everything that Task returns. When you receive the Task , it may already be completed.
However, this alone does not mean that it worked synchronously. In fact, this assumes that it starts asynchronously and is complete before you receive an instance of Task .
Remember that you can await to complete an already completed task:
private static async Task CallFooAsync() { await FooAsync(); } private static Task FooAsync() { return Task.CompletedTask; } private static void Main() { CallFooAsync().Wait(); }
Also, if the async method does not have await , it will work synchronously.
Note. As you already know, the method that Task returns may be waiting on the network or in the file system, etc ... this does not mean that you are starting a new Thread or putting something on ThreadPool .
In the context of synchronization, which is processed by a single thread, the result will be the execution of the Task synchronously with some overhead. This concerns the user interface flow, I will talk more about what happens below.
You can write a custom TaskScheduler to always run tasks synchronously. In the same thread that makes the call.
Note. I recently wrote a custom SyncrhonizationContext that runs tasks in a single thread. You can find it in Creating a Task Scheduler (System.Threading.Tasks.) . This will result in such a TaskScheduler with a call to FromCurrentSynchronizationContext .
By default, TaskScheduler will call ThreadPool calls. However, when you wait from an operation if it was not running on ThreadPool , it will try to remove it from ThreadPool and start it inline (in the same thread that is waiting ... the thread is still waiting, so it is not busy).
Note. One notable exception is Task , noted by LongRunning . LongRunning Task will work in a separate thread .
Your question
If I have a lot of CPU-related work (let's say it does a lot of tough math), should this task asynchronously block some threads correctly? Something must actually do the math. If I wait for this, then the thread will be blocked.
If you perform calculations, they must happen in some thread, this part of the right.
However, the beauty of async and await is that the waiting thread should not be blocked (more on this later). However, it is very easy to shoot in the leg, expecting the expected task to be launched in the same thread that is waiting, which will lead to synchronous execution (which is an easy mistake in the user interface thread).
One of the key characteristics of async and await is that they accept a SynchronizationContext from the caller. For most threads that lead to the use of the standard TaskScheduler (which, as mentioned earlier, uses ThreasPool ). However, for the user interface thread, this means that tasks are sent to the message queue, which means that they will be executed in the user interface thread. The advantage of this is that you do not need to use Invoke or BeginInvoke to access the user interface components.
Before moving on to await a Task from a user interface thread without blocking it, I want to note that you can implement TaskScheduler , if you are await on a Task , you don’t lock your thread or stand idle, instead you let your thread select another Task that is waiting to be executed . When I was backporting Tasks for.NET 2.0 I experimented with this.
What is an example of an asynchronous method and how will they work? Are they limited for I / O operations that use some hardware, so the thread is never blocked?
It seems you got confused asynchronously without blocking the thread. , , .NET, , , , - await . , , TaskScheduler.FromCurrentSynchronizationContext .
. Timer , Application.Idle - .
async , , . . await , Task , async ( ). Task , ( await ).
, , await , . Task , ( await ) . await .
async await , .
. Windows Forms, Button TextBox :
private async void button1_Click(object sender, EventArgs e) { await WorkAsync(5000); textBox1.Text = @"DONE"; } private async Task WorkAsync(int milliseconds) { Thread.Sleep(milliseconds); }
. , , , await SynchronizationContext . . WorkAsync .
Here's what happens:
await WorkAsync(5000)WorkAsync(5000) ( ) , ... , .- .
WorkAsync(5000)WorkAsync(5000)WorkAsync Thread.Sleep . 5 .- , .
- .
.
, Task.Delay . ; Sleep . , async await , . , (, ThreadPool ) .
:
private async void button1_Click(object sender, EventArgs e) { await Task.Run(() => Work(5000)); textBox1.Text = @"DONE"; } private void Work(int milliseconds) { Thread.Sleep(milliseconds); }
, . , Thread.Sleep ThreadPool Task.Run . button1_Click async , await , . Task await , , .
Here's what happens:
await Task.Run(() => Work(5000))Task.Run(() => Work(5000)) ( ) , ... , .- .
Task.Run(() => Work(5000)) ,Task.Run(() => Work(5000)) , ThreadPool- .
ThreadPool , , . , .