C#编译器如何知道何时切断异步方法?

时间:2017-11-12 17:14:18

标签: c# .net asynchronous async-await

我试图更多地了解async/await,特别是编译器如何知道"暂停"使用async方法和await而不会产生其他线程。

举个例子,我们说我有async方法,比如

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

我知道await sqlConnection.OpenAsync();是我的方法获取的地方"暂停"并且调用它的线程返回到线程池,一旦跟踪连接开放的Task完成,就会发现可用线程运行DoSomeOtherStuff()

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

这里我感到困惑。我查看OpenAsynchttps://referencesource.microsoft.com/#System.Data/System/Data/Common/DBConnection.cs,e9166ee1c5d11996,references)的源代码及其

    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;
    }

我想要看到编译器知道的某个地方&#34;切断&#34;线程,因为任务已经开始与外部资源进行通信,但我并没有真正看到它,事实上,Open();似乎意味着它正在同步等待。有人可以解释这是如何变得无线的&#34;真正的异步&#34;码?

2 个答案:

答案 0 :(得分:6)

您的方法不一定会被暂停&#34;在等待。如果您正在等待的任务已经完成(您提供的代码中的情况) - 方法将照常继续。您正在查看的方法实际上不是SqlConnection使用的方法,因为DbConnection是基类,方法OpenAsync是虚拟的。 SqlConnection覆盖它并提供真正的异步实现。但是,并非所有提供商都这样做,而那些没有提供者确实会使用您在问题中显示的实现。

当使用这样的实现时 - 整个事情将在没有任何线程切换的情况下同步运行。假设你有

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

您使用的提供程序不提供OpenAsync的真正异步版本。然后当有人拨打await Do()时 - 调用线程将执行所有工作(DoSomeStuffOpenAsyncDoSomeOtherStuff)。如果那是UI线程 - 它将在整个持续时间内被阻止(这种情况经常发生在人们使用&#34; async&#34; UI线程中的此类提供者的方法时,假设它会以某种方式将工作从UI线程中移除,不会发生。)

答案 1 :(得分:5)

使用async-await的好处是由调用以下

的线程引起的
await sqlConnection.OpenAsync();

将被释放,并且可以从属于的线程池中使用它。如果我们讨论的是ASP.NET应用程序,那么该线程将被释放并可用于提供另一个传入的HTTP请求。这样,ASP.NET线程池的线程将始终可用于服务HTTP请求,并且不会阻塞例如I / O,例如打开与数据库的连接并执行某些SQL语句。

<强>更新

这里应该说明,如果您要完成的任务await已完成,您的代码将同步运行。