async w / await与同步调用有何不同?

时间:2013-06-22 11:07:40

标签: c# .net asynchronous async-await c#-5.0

我正在阅读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();

2 个答案:

答案 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()后才会执行。 (请参阅thisthis)。

现在,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}}阻止。

我希望这可以解除你的疑虑。