How does the C # compiler know when to disable the async method?

I am trying to understand something else about async/await and especially how the compiler knows to β€œsuspend” the async and await method without creating additional threads.

As an example, suppose I have an async method like

 DoSomeStuff(); await sqlConnection.OpenAsync(); DoSomeOtherStuff(); 

I know await sqlConnection.OpenAsync(); - this is where my method gets suspended, and the thread it calls returns to the thread pool, and as soon as the Task that monitors the connection is opened, then the available DoSomeOtherStuff() thread is available.

 | DoSomeStuff() | "start" opening connection | ------------------------------------ | | ---------------------------------------------------------- | DoSomeOtherStuff() - | 

Here where I am confused. I look at the source code of OpenAsync ( https://referencesource.microsoft.com/#System.Data/System/Data/Common/DBConnection.cs,e9166ee1c5d11996,references ) and it

  public Task OpenAsync() { return OpenAsync(CancellationToken.None); } public virtual Task OpenAsync(CancellationToken cancellationToken) { TaskCompletionSource<object> taskCompletionSource = new TaskCompletionSource<object>(); if (cancellationToken.IsCancellationRequested) { taskCompletionSource.SetCanceled(); } else { try { Open(); taskCompletionSource.SetResult(null); } catch (Exception e) { taskCompletionSource.SetException(e); } } return taskCompletionSource.Task; } 

I assume that I will see a place where the compiler would know to "cut off" the stream, because the task began to communicate with an external resource, but I really do not see this, and in fact Open(); seems to mean that he is waiting synchronously. Can someone explain how this becomes modeless "true asynchronous" code?

+5
source share
2 answers

Your method does not necessarily "pause" on hold. If the task you are expecting is already completed (the case with the code that you specified), the method will continue, as usual. The method you are looking at is actually not the one used by SqlConnection , because DbConnection is the base class, and the OpenAsync method is virtual. SqlConnection cancels it and provides a real asynchronous implementation. However, not all providers do this, and those who do not do this will actually use the implementation that you show in your question.

When such an implementation is used, everything will work synchronously without any threads. Suppose you have

 public async Task Do() { DoSomeStuff(); await sqlConnection.OpenAsync(); DoSomeOtherStuff(); } 

And you are using a provider that does not provide a true asynchronous version of OpenAsync . Then, when someone calls await Do() , the calling thread will do all the work ( DoSomeStuff , OpenAsync , DoSomeOtherStuff ). If this is a UI thread, it will be blocked for the entire duration (this situation often happens when people use "asynchronous" methods for such providers in the user interface thread, assuming that somehow they will postpone the work from the user interface thread, which will not happen) .

+6
source

The advantage of using async waiting is due to the fact that the thread causing the following

 await sqlConnection.OpenAsync(); 

will be released and will be available for use from the thread pool to which it belongs. If we are talking about an ASP.NET application, the thread will be freed and will be available to serve another incoming HTTP request. Thus, ASP.NET thread pool threads will always be available to serve HTTP requests and will not block, for example, for I / O, for example, opening a database connection and executing some SQL statement.

Update

It should be stated here that if the task you are about to complete await is completed, your code will work synchronously.

+5
source

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


All Articles