我正在阅读http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx?cs-save-lang=1&cs-lang=csharp上的异步函数调用。
在第一个例子中,他们这样做,我得到了:
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();
答案 0 :(得分:11)
调用await client.GetStringAsync()
会导致执行调用方法,这意味着它不会等待方法完成执行,因此不会阻塞线程。一旦在后台执行完毕,该方法将从停止的地方继续。
如果你只是调用client.GetString()
,线程的执行将不会继续,直到此方法完成执行,这将阻止线程并可能导致UI无响应。
示例:
public void MainFunc()
{
InnerFunc();
Console.WriteLine("InnerFunc finished");
}
public void 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
}
答案 1 :(得分:8)
根据我的理解,await关键字将暂停代码流,直到函数返回
嗯,是和否。
client.GetString()
将阻止该线程。)实际上,它将返回其调用方法。要通过返回其调用方法来理解它的含义,您可以阅读另一个C#编译器魔术 - yield return
语句。
使用yield return
的迭代器块会将方法分解为状态机 - yield return
语句之后的代码只有在枚举器上调用MoveNext()
后才会执行。 (请参阅this和this)。
现在,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
建立在Task并行库之上 - 因此,此 Scheduling 是通过TPL完成的。code block 2
可能不会在同一个线程Thread1
上进行调度,除非它具有与线程相关的活动SynchronizationContext
(如WPF / WinForms UI线程)。 await
SynchronizationContext
具有code block 2
意识,因此SynchronizationContext
会在调用MyMethodAsync()
时安排在同一SynchronizationContext
(如果有)上。如果没有活跃的code block 2
,那么尽管有可能,async/await
将会运行一些不同的主题。最后,我会说,因为yield return
基于编译器创建的状态机,如await
,它分享了一些缺点 - 例如,你不能在finally
内部{{1}}阻止。
我希望这可以解除你的疑虑。