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

Rij*_*ijk 17 .net c# 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;
Run Code Online (Sandbox Code Playgroud)

但他们解释说,如果没有任何工作要做,你可以这样做:

string urlContents = await client.GetStringAsync();
Run Code Online (Sandbox Code Playgroud)

根据我的理解,await关键字将暂停代码流,直到函数返回.那么这有什么不同:

string urlContents = client.GetString();
Run Code Online (Sandbox Code Playgroud)

Adi*_*ter 13

调用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
}
Run Code Online (Sandbox Code Playgroud)

  • @Rijk:异步函数不一定用“async”标识符标记 - 并且不一定发出“await”。返回“Task”或“Task&lt;T&gt;”的普通函数可以被视为异步函数,您可以对其进行“await”。 (3认同)
  • 当我读到"同步和异步行为之间的区别是,同步方法在其工作完成时返回,但异步方法_在其工作被暂停时返回任务值"时,我终于明白了." 所以异步函数仍然会像同步函数一样运行,*直到*它们发出一个`await`语句,它会将控制返回给调用者. (2认同)

YK1*_*YK1 9

据我所知,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
}
Run Code Online (Sandbox Code Playgroud)
  • 当您使用async标识符标记方法时,您会告诉编译器将方法分解为状态机,并且您将await进入此方法.
  • 让我们说代码在一个线程上运行Thread1,你的代码调用它MyMethodAsync().然后code block 1将在同一个线程上同步运行.
  • SomeAwaitableMethodAsync()也将同步调用 - 但是假设该方法启动一个新的异步操作并返回一个Task.
  • 这是await进入画面的时候.它会将代码流返回给调用者,并且线程Thread1可以自由运行调用者代码.那么,什么发生在调用方法取决于调用方法是否awaitS于MyMethodAsync()或做别的东西-但重要的是Thread1不会被阻断.
  • 现在,等待着的魔法其余的-当返回的任务SomeAwaitableMethodAsync()最终完成,code block 2计划运行.
  • async/await基于任务并行库构建 - 因此,此调度是通过TPL完成的.
  • 现在的问题是,除非它具有与线程关联的活动(如WPF/WinForms UI线程),否则code block 2可能无法在同一个线程上进行调度.被察觉,因此,预计在相同的,如果有的话,当被调用.如果没有活动,那么尽管有可能,将会运行一些不同的线程.Thread1SynchronizationContextawaitSynchronizationContextcode block 2SynchronizationContextMyMethodAsync()SynchronizationContextcode block 2

最后,我会说,既然async/await是基于编译器创建的状态机,比如yield return,它分享了一些缺点 - 例如,你不能await在一个finally块里面.

我希望这能解决你的疑虑.