async with await与同步调用有何不同?
本文关键字:何不同 调用 同步 with await async | 更新日期: 2023-09-27 18:19:25
我正在阅读异步编程中使用Async和Await的异步函数调用。
在第一个例子中,他们这样做,我得到:
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
string urlContents = await getStringTask;
但他们解释说,如果在此期间没有任何工作要做,你可以这样做:
string urlContents = await client.GetStringAsync();
据我所知,await
关键字将暂停代码流,直到函数返回。那么,这与下面的同步调用有什么不同呢?
string urlContents = client.GetString();
调用await client.GetStringAsync()
将执行给调用方法,这意味着它不会等待方法完成执行,因此不会阻塞线程。一旦在后台执行完成,该方法将从它停止的地方继续执行。
如果你只调用client.GetString()
,线程的执行将不会继续,直到这个方法完成执行,这将阻塞线程,并可能导致UI变得无响应。
的例子:
public void MainFunc()
{
InnerFunc();
Console.WriteLine("InnerFunc finished");
}
public async Task InnerFunc()
{
// This causes InnerFunc to return execution to MainFunc,
// which will display "InnerFunc finished" immediately.
string urlContents = await client.GetStringAsync();
// Do stuff with urlContents
}
public void InnerFunc()
{
// "InnerFunc finished" will only be displayed when InnerFunc returns,
// which may take a while since GetString is a costly call.
string urlContents = client.GetString();
// Do stuff with urlContents
}
根据我的理解,await关键字将暂停代码流,直到函数返回
好吧,是和不是。
- 是的,因为代码流在某种意义上确实停止了。不,因为执行这个代码流的线程没有阻塞。(同步调用
client.GetString()
会阻塞线程)。
实际上,它将返回到它的调用方法。要理解return到它的调用方法是什么意思,你可以阅读另一个c#编译器的魔法——yield return
语句。
带有yield return
的迭代器块将把方法分解成一个状态机——只有在枚举器上调用MoveNext()
之后,yield return
语句之后的代码才会执行。(见这个和这个)。
现在,async/await
机制也是基于类似的状态机(然而,它比yield return
状态机复杂得多)。
为了简化问题,我们考虑一个简单的异步方法:
public async Task MyMethodAsync()
{
// code block 1 - code before await
// await stateement
var r = await SomeAwaitableMethodAsync();
// code block 2 - code after await
}
- 当你用
async
标识符标记一个方法时,你告诉编译器把这个方法分解成一个状态机,并且你要在这个方法中使用await
。假设代码运行在一个线程Thread1
上,你的代码调用这个线程MyMethodAsync()
。然后code block 1
将在同一线程上同步运行。 -
SomeAwaitableMethodAsync()
也将被同步调用——但是让我们假设该方法启动一个新的异步操作并返回一个Task
。 - 这是
await
进入图片的时候。它将代码流返回给它的调用者,并且线程Thread1
可以自由地运行调用者代码。调用方法时发生的情况取决于调用方法await
是否在MyMethodAsync()
上或做其他事情-但重要的是Thread1
不会被阻塞。 - 现在等待魔法的休息-当
SomeAwaitableMethodAsync()
返回的任务最终完成时,code block 2
被调度运行。 -
async/await
是建立在任务并行库上的——所以,这个调度是通过TPL完成的。 - 现在的事情是,这个
code block 2
可能不会被安排在同一个线程Thread1
上,除非它有一个活动的SynchronizationContext
与线程的亲和力(如WPF/WinForms UI线程)。await
是SynchronizationContext
感知的,因此,当MyMethodAsync()
被调用时,code block 2
被调度到相同的SynchronizationContext
上(如果有的话)。如果没有活动的SynchronizationContext
,那么code block 2
将在其他线程上运行。
最后,我要说的是,由于async/await
是基于由编译器创建的状态机,就像yield return
一样,它有一些缺点——例如,你不能在finally
块中使用await
。
我希望这能消除你的疑虑。