Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does a C# compiler know when to cut off an async method?

I'm trying to understand some more about async/await and particularly how the compiler knows to "pause" at an async method and await without spawning additional threads.

As an example, let's say I have an async method like

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

I know that await sqlConnection.OpenAsync(); is where my method gets "suspended" and the thread that invoked it returns to the thread pool and once the Task that is tracking the connection opening completes then an available thread is found to run DoSomeOtherStuff().

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

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

    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 imagine to see some place where the compiler would know to "cut off" the thread because the task has begun communicating with an external resource, but I don't really see that, and in fact, the Open(); seems to imply that it is synchronously waiting. Can someone explain how this becomes threadless "true async" code?

like image 898
Questionaire Avatar asked Nov 12 '17 17:11

Questionaire


2 Answers

Your method does not necessarily gets "suspended" on await. If task you are awaiting is already completed (the case in code you provided) - method will just continue as usual. The method you are looking at is really not what is used by SqlConnection, because DbConnection is base class and method OpenAsync is virtual. SqlConnection overrides it and provides real asynchronous implementation. However, not all providers do that and those who do not will indeed use implementation you show in your question.

When such implementation is used - the whole thing will run synchronously without any thread switches. Suppose you have

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

And you use provider which does not provide real async version of OpenAsync. Then when someone calls await Do() - calling thread will perform all the work (DoSomeStuff, OpenAsync, DoSomeOtherStuff). If that is UI thread - it will be blocked for the whole duration (such situation happens often when people use "async" methods for such providers in UI thread assuming that will somehow put the work off the UI thread, which does not happen).

like image 90
Evk Avatar answered Nov 15 '22 23:11

Evk


The benefit of using async-await results from the fact the thread that called the following

await sqlConnection.OpenAsync();

would be released and it would be available to be utilized by from the thread pool that belongs to. If we are talking about for an ASP.NET application the thread would be freed and would be available to serve another incoming HTTP request. This way the threads of the ASP.NET thread pool would be always available to service HTTP requests and wouldn't block for instance for an I/O, like opening a connection to a database and executing some SQL statement.

Update

It should be stated here that if the task you are going to await has been completed, your code would run synchronously.

like image 44
Christos Avatar answered Nov 16 '22 00:11

Christos