This is the 19th day of my participation in the August Wenwen Challenge.More challenges in August

  • 📢 welcome to like: 👍 collect ⭐ message 📝 if there are mistakes please correct, give people rose, hand left lingering fragrance!
  • 📢 This article was originally written by Webmote and originally published by Nuggets.
  • 📢 author’s motto: life is toss about, when you don’t toss about life, life will start to toss about you, let us come on together! 💪 💪 💪

Preface 🎏

Asp.net and the.NET Framework class libraries have been interfering with many of the weirdest things that are said and done about async programming in.net Core. As a result, a large number of newbies to.net core console/asp.net core also have a deep misunderstanding of how to handle asynchrony, so digging into the essential differences would be enormously helpful for myself and many more newbies to.net core.

🎏 5. You do not need ConfigureAwait (false) and use it wherever possible in your authoring library

ConfigureAwait(false) is not required because there is no longer a context. Any code that knows it runs under ASP.NET Core doesn’t need to explicitly avoid its context. In fact, the ASP.NET Core team itself has abandoned the use of ConfigureAwait(false).

However, I still recommend you use it in your core library – anything that can be reused in other applications. ConfigureAwait(False) should still be used in the library if the code in the library can also be run in a UI application or an older ASP.NET application, or in any other environment where a context might exist.

🎏 6. Beware of implicit parallelism

There is another major issue when moving from synchronous context to context on the thread pool (that is, from older ASP.NET to ASP.NET Core).

Old ASP.NET SynchronizationContext is the actual synchronization context, this means that the request context, can only have a thread actually execute the code. That is, asynchronous continuations can run on any thread, but only one at a time. ASP.NET Core has no SynchronizationContext, so await defaults to context on the thread pool. Thus, in the ASP.NET Core world, asynchronous continuations can run on any thread, and they can all run in parallel.

As an artificial example, consider the code that downloads two strings and places them in a list. This code works fine in older ASP.NET because the request context only allows one sequence at a time:

private HttpClient _client = new HttpClient();

async Task<List<string>> GetBothAsync(string url1, string url2)
{
    var result = new List<string> ();var task1 = GetOneAsync(result, url1);
    var task2 = GetOneAsync(result, url2);
    await Task.WhenAll(task1, task2);
    return result;
}

async Task GetOneAsync(List<string> result, string url)
{
    var data = await _client.GetStringAsync(url);
    result.Add(data);
}
Copy the code

The result.add (data) line can only be executed by one thread ata time because it is executed in the request context.

However, the same code is not secure on ASP.NET Core. Specifically, the result.add (data) line can be executed simultaneously by two threads without protecting the shared List.

Such code is rare; Asynchronous code has its own functionality, so it’s much more natural to return results from asynchronous methods than to modify shared state. However, the quality of asynchronous code does vary, and there is no doubt that some of it does not adequately shield parallel execution.

7. Do asynchronous tasks require threads?

Asynchronous tasks do not “execute”, so no thread is required. This is the purest basic truth of asynchrony: there are no threads. There are a great many white people who oppose this truth. They shout: “No, if I’m waiting for an operation, there must be a waiting thread! This could be a thread pool thread. Operating system threads! Or something with a device driver…”

The asynchronous task mentioned here is simply designated as an IO intensive operation, while the CPU intensive operation is synchronous in nature. A thread can pretend to be asynchronous by loading it into another thread. For example, UI threads can pretend to be asynchronous by wrapping CPU-bound code in task.run. From the perspective of the UI thread, it appears to be asynchronous (that is, it can wait for operations). But from the point of view of the other thread (thread pool thread), it is still synchronous, so it is clear that we are referring to IO intensive true asynchrony.Don’t listen to that nonsense. If asynchronous operations are pure operations, there are no threads.

They don’t believe me. Let’s tease them.

We will trace all the way down to asynchronous operations on the hardware, especially with care. NET part and device driver part. We will simplify this description by omitting some middle-tier details, but we will not deviate from the facts. Consider a generic “write” operation (for files, network streams, USB toasters, etc.). Our code is simple:

private async void Button_Click(object sender, RoutedEventArgs e)
{
  byte[] data = ...
  await myDevice.WriteAsync(data, 0, data.Length);
}
Copy the code

We already know that the UI thread is not blocking await during this time. Question: Does another thread have to sacrifice itself on the “block altar” so that the UI thread can survive?

First stop: library (for example, enter BCL code). Let’s assume that WriteAsync is used. NET based on overlapping I/O standard P/Invoke asynchronous I/O system. Therefore, this will start the Win32 overlapping I/O operation HANDLE at the bottom of the device.

The operating system then turns to the device driver and asks it to start writing. To do this, you first construct an object that represents the write request. This is called an I/O Request packet (IRP).

The device driver receives the IRP and issues commands to the device to write out the data. If the device supports direct memory access (DMA), it can be as simple as writing buffer addresses to device registers. This is what the device driver can do; It marks the IRP as pending and returns to the operating system.

The heart of the matter is here: device drivers are not allowed to block when handling IRP. This means that if the IRP cannot be completed immediately, then it must be processed asynchronously. This is true even for synchronous apis! At the device driver level, all requests are asynchronous.

In the case of “suspended” IRP, the operating system returns to the library, which returns the unfinished task to the button-click event handler, which suspends the Async method, and the UI thread continues execution.

We’ve traced requests deep into the system, all the way to the physical device.

The write operation is now in the “running” state. How many threads are being processed? The answer is no threads. No device driver thread, OS thread, BCL thread, or thread pool thread is processing the write operation. There are no threads.

Now let’s focus on the kernel daemon realm’s response to the mortal world.

A period of time after the write request starts, the device completes the write. Notify the CPU with interrupts.

The interrupt Service routine (ISR) of the device driver responds to this interrupt. Interrupts are CPU-level events that temporarily take control of the CPU away from whatever threads are running. You can think of ISRs as “borrowing” threads that are currently running, but I prefer to think of ISRs as having such a low level of execution that there is no such thing as a “thread” – so they are all “below” threads, so to speak.

In any case, the ISR is written correctly, so all it has to do is tell the device “thank you for your interruption” and queue the deferred procedure call (DPC).

When the CPU is plagued by interrupts, it enters its DPC. The level of DPC execution is also too low to say “thread”. Like ISR, DPC executes directly on the CPU “below” the threaded system.

The DPC receives the IRP representing the write request and marks it as done. However, the “done” state exists only at the OS level. Processes have their own memory space that must be notified. Therefore, the operating system queues the special kernel-mode asynchronous procedure call (APC) to the HANDLE in its own thread.

Because the library/BCL uses a standard P/Invoke overlapping I/O system, it has registered handles to a portion of the THREAD pool’s I/O completion port (IOCP). Therefore, an I/O thread pool thread is briefly borrowed to execute APC, which notifies the task that it is complete.

The task has captured the UI context, so it does not async directly restore the method on the thread pool thread. Instead, the method’s continuity is queued into the UI context, and the UI thread will continue executing the method when it reaches it.

Therefore, we see no threads during the request. After the request is completed, individual threads are “borrowed” or work is briefly queued. This work typically takes about a millisecond (for example, APC running in a thread pool) to about a millisecond (for example, ISR). But no thread is blocked, just waiting for the request to complete.

Free your mind. Don’t try to find this “asynchronous thread” — it’s impossible. Instead, just try to understand the facts: there are no threads.

🎏 8. Code after await runs on the thread pool

Also, any code after await will run on the thread pool thread. This is the default behavior TaskScheduler unless there is a SynchronizationContext or.

These are the most common contexts:

  • UI context – The UI application uses this to ensure that subsequent code await will be restored on the UI thread.
  • ASP.NET Classic request context – used by ASP.NET Classic to ensure that subsequent code await will recover with the same request-specific global properties (such as httpcontext.current).
  • Single-threaded context – used by some test framework (such as xUnit) to ensure that subsequent code await will be restored on the same thread. This is intended to simulate UI context behavior, but there is no full UI thread.
  • Context on the thread pool – default if no other context exists (for example, ASP.NET Core and console applications). Await the code after running on the thread pool thread.

🎏 9. Summary

Well, that’s the end of this piece.

Routine summary, rational view!

Knot is what ah, knot is I want you to praise but can not get lonely. 😳 😳 😳

👓 have seen this, but also care about a thumbs-up?

👓 has been liked, but also care about a collection?

👓 are collected, but also care about a comment?