C# 异步w/await与同步调用有何不同?
我在读关于异步函数调用的文章 在第一个例子中,他们这样做,我得到:C# 异步w/await与同步调用有何不同?,c#,.net,asynchronous,async-await,c#-5.0,C#,.net,Asynchronous,Async Await,C# 5.0,我在读关于异步函数调用的文章 在第一个例子中,他们这样做,我得到: 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 ge
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;
据我所知,wait
关键字将暂停代码流,直到函数返回。那么这与:
string urlContents = client.GetString();
?调用
wait 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
}
据我所知,await关键字将暂停代码流,直到函数返回
嗯,是和否
- 是的,因为代码流在某种意义上确实停止了
- 否,因为执行此代码流的线程不会阻塞。(同步调用
将阻塞线程)client.GetString()
yield return
语句
带有yield return
的迭代器块将把方法分解成一个状态机-其中yield return
语句后面的代码将仅在对枚举器调用MoveNext()
后执行。(见和)
现在,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
- 假设代码在线程
上运行,您的代码调用此Thread1
。然后MyMethodAsync()
将在同一线程上同步运行李>代码块1
也将被同步调用-但假设该方法启动一个新的异步操作并返回一个SomeAwaitableMethodAsync()
李>任务
- 这是
进入画面的时候。它会将代码流返回给调用者,线程wait
可以自由运行调用者代码。调用方法时会发生什么,取决于调用方法Thread1
s是在wait
上,还是执行其他操作-但重要的是MyMethodAsync()
未被阻止Thread1
- 现在,等待的其余神奇之处是:当
返回的任务最终完成时,SomeAwaitableMethodAsync()
计划运行代码块2
构建在任务并行库上-因此,此调度通过TPL完成async/await
- 现在的问题是,这个
可能不会在同一个线程代码块2
上调度,除非它有一个活动的Thread1
与线程关联(如WPF/WinForms UI线程)SynchronizationContext
是await
感知的,因此,调用SynchronizationContext
时,MyMethodAsync()
在相同的code块2
上调度。如果没有活动的SynchronizationContext
,则极有可能,SynchronizationContext
将在某些不同的线程上运行代码块2
async/await
是基于编译器创建的状态机的,就像yield return
,它有一些缺点-例如,您不能在finally
块中await
我希望这能消除您的疑虑。它不会阻止主执行上下文/线程,因此例如UI不会被冻结(在使用UI的应用程序中)。但这主要是因为该方法本身是异步的,对吗?这难道不意味着只要它忙于工作,调用线程就可以继续吗?不,需要异步,这样它就可以调用wait。所有的魔法都发生在wait调用上。啊,所以如果该方法调用一个“常规”慢函数,它仍然会阻塞主线程?当您编写wait时,该函数中调用后的所有行在异步函数执行完成之前都不会执行。我在读时终于得到了它同步和异步行为的区别在于,同步方法在其工作完成时返回,而异步方法在其工作暂停时返回任务值。“。因此异步函数的行为仍将类似于同步函数,直到它们发出一个
await
语句,该语句将控制权返回给调用者。@Rijk:异步函数不一定用async
标识符标记,也不一定发出await
。返回任务
或任务
的正常函数可以视为异步函数,您可以等待
。出色的回答感谢您的详细回答!
public async Task MyMethodAsync()
{
// code block 1 - code before await
// await stateement
var r = await SomeAwaitableMethodAsync();
// code block 2 - code after await
}