Async expects processor usage for IO?

I already know that async-await maintains a thread context, also handles exception forwarding, etc. (which helps a lot).

But consider the following example:

 /*1*/ public async Task<int> ExampleMethodAsync() /*2*/ { /*3*/ var httpClient = new HttpClient(); /*4*/ /*5*/ //start async task... /*6*/ Task<string> contentsTask = httpClient.GetStringAsync("http://msdn.microsoft.com"); /*7*/ /*8*/ //wait and return... /*9*/ string contents = await contentsTask; /*10*/ /*11*/ //get the length... /*12*/ int exampleInt = contents.Length; /*13*/ /*14*/ //return the length... /*15*/ return exampleInt; /*16*/ } 

If the async method ( httpClient.GetStringAsync ) is an I / O operation (as in my example above) So - I get the following things:

  • Caller Topic is not locked
  • The workflow is released because there is an I / O operation (I / O completion ports ...) (GetStringAsync uses TaskCompletionSource and does not open a new thread)
  • Specific thread context
  • Exception thrown

But what if, instead of httpClient.GetStringAsync (I / O operation), I have the CalcFirstMillionsDigitsOf_PI_Async Task (heavy computation operation in a sperate stream)

It seems that the only thing I get here is:

  • Specific thread context
  • Exception thrown
  • Caller Topic is not locked

But I still have another thread (parallel thread) that performs the operation. and the processor switches between the main thread and the operation.

Is my diagnosis correct?

+4
source share
3 answers

In fact, you get only the second set of benefits in both cases. await does not start asynchronous execution of anything, it is just a keyword for the compiler to generate code to handle completion, context, etc.

You can find a better explanation of this in "Call the method with the wait" ... ugh! 'from Stephen Toub.

Up to the asynchronous method itself, to decide how it achieves asynchronous execution:

  • Some methods will use the task to run their code in the ThreadPool thread.
  • Some will use some mechanism for completing I / O. There is even a special ThreadPool for this, which you can use with Tasks using the custom TaskScheduler
  • Some of them wrap the TaskCompletion object through another mechanism, such as events or callbacks.

In each case, a particular implementation frees the thread (if used). TaskScheduler automatically frees the thread when the task completes execution, so you get this functionality for cases No. 1 and No. 2 in any case.

What happens in case # 3 for callbacks depends on how the callback is made. In most cases, the callback is executed in a thread controlled by some external library. In this case, you need to quickly process the callback and return so that the library can reuse this method.

EDIT

Using the decompiler, you can see that GetStringAsync uses the third option: it creates a TaskCompletion source that receives a signal when the operation completes. The execution of the operation is delegated to the HttpMessageHandler.

+3
source

Your analysis is correct, although the wording of your second part sounds as if async creates a workflow for you, but it is not.

In the library code, you really want to synchronize synchronous synchronous methods. If you want to use the synchronous method asynchronously (for example, from a user interface thread), then call it using await Task.Run(..)

+3
source

Yes you are right. I can not find the wrong statement in your question. The term "Canned thread context" is not clear to me. Do you mean the logical control flow? In this case, I would agree.

As for the processor-bound example: you usually don’t do this, because starting a CPU-based task and waiting for it increases overhead and reduces throughput. But this may be true if you need the caller to be unlocked (for example, in the case of a WinForms or WFP project).

+1
source

Source: https://habr.com/ru/post/1498916/


All Articles