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()
    将阻塞线程)
实际上,它将返回其调用方法。要理解返回其调用方法意味着什么,您可以阅读另一个C#编译器魔术--
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
    可以自由运行调用者代码。调用方法时会发生什么,取决于调用方法
    wait
    s是在
    MyMethodAsync()
    上,还是执行其他操作-但重要的是
    Thread1
    未被阻止
  • 现在,等待的其余神奇之处是:当
    SomeAwaitableMethodAsync()
    返回的任务最终完成时,
    代码块2
    计划运行
  • async/await
    构建在任务并行库上-因此,此调度通过TPL完成
  • 现在的问题是,这个
    代码块2
    可能不会在同一个线程
    Thread1
    上调度,除非它有一个活动的
    SynchronizationContext
    与线程关联(如WPF/WinForms UI线程)
    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
}