我试图更多地了解async/await
,特别是编译器如何知道"暂停"使用async
方法和await
而不会产生其他线程。
举个例子,我们说我有async
方法,比如
DoSomeStuff();
await sqlConnection.OpenAsync();
DoSomeOtherStuff();
我知道await sqlConnection.OpenAsync();
是我的方法获取的地方"暂停"并且调用它的线程返回到线程池,一旦跟踪连接开放的Task
完成,就会发现可用线程运行DoSomeOtherStuff()
。
| DoSomeStuff() | "start" opening connection | ------------------------------------ |
| ---------------------------------------------------------- | DoSomeOtherStuff() - |
这里我感到困惑。我查看OpenAsync
(https://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;码?
答案 0 :(得分:6)
您的方法不一定会被暂停&#34;在等待。如果您正在等待的任务已经完成(您提供的代码中的情况) - 方法将照常继续。您正在查看的方法实际上不是SqlConnection
使用的方法,因为DbConnection
是基类,方法OpenAsync
是虚拟的。 SqlConnection
覆盖它并提供真正的异步实现。但是,并非所有提供商都这样做,而那些没有提供者确实会使用您在问题中显示的实现。
当使用这样的实现时 - 整个事情将在没有任何线程切换的情况下同步运行。假设你有
public async Task Do() {
DoSomeStuff();
await sqlConnection.OpenAsync();
DoSomeOtherStuff();
}
您使用的提供程序不提供OpenAsync
的真正异步版本。然后当有人拨打await Do()
时 - 调用线程将执行所有工作(DoSomeStuff
,OpenAsync
,DoSomeOtherStuff
)。如果那是UI线程 - 它将在整个持续时间内被阻止(这种情况经常发生在人们使用&#34; async&#34; UI线程中的此类提供者的方法时,假设它会以某种方式将工作从UI线程中移除,不会发生。)
答案 1 :(得分:5)
使用async-await的好处是由调用以下
的线程引起的await sqlConnection.OpenAsync();
将被释放,并且可以从属于的线程池中使用它。如果我们讨论的是ASP.NET应用程序,那么该线程将被释放并可用于提供另一个传入的HTTP请求。这样,ASP.NET线程池的线程将始终可用于服务HTTP请求,并且不会阻塞例如I / O,例如打开与数据库的连接并执行某些SQL语句。
<强>更新强>
这里应该说明,如果您要完成的任务await
已完成,您的代码将同步运行。