繁体   English   中英

.NET异步\等待基础

[英].NET async\await fundamentals

在使用.NET 4.5 async \\ await框架后,我有一个问题。

我们来看看这个程序( msdn示例 ):

    async Task<int> AccessTheWebAsync()
    {
        HttpClient client = new HttpClient();

        Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

        DoIndependentWork();

        string urlContents = await getStringTask;

        return urlContents.Length;
    }


    void DoIndependentWork()
    {
        resultsTextBox.Text += "Working . . . . . . .\r\n";
    }

该程序将按以下顺序运行:

  1. 新的HttpClient。
  2. GetStringAsync同步调用。
  3. 在某些时候,GetStringAsync调用await,控件返回AccessTheWebAsync。
  4. 调用了DoIndependentWork。
  5. 程序等待返回的字符串(如果操作没有完成则阻塞)。
  6. 返回urlContent的长度。

我花了一些时间来理解的一件事是GetStringAsync方法尽管它的名称同步运行(名称约定实际上是误导性的)。

在我们异步运行该方法的过程中,我们需要显式地使用Task.RunTask.Factory.StartNew

但真正的问题是,如果我们有独立的工作,为什么不马上做,而不是等待等待从GetStringAsync调用? (换句话说,为什么异步方法不能按定义异步运行?)

编辑:我将改写第二和第三个操作:

(2)GetStringAsync同步启动。

(3)在某些时候,GetStringAsync调用await和线程分叉,控件返回AccessTheWebAsync。

我花了一些时间来理解的一件事是GetStringAsync方法尽管它的名称同步运行(名称约定实际上是误导性的)。

那是不对的。 GetStringAsync返回Task<string> 它将立即返回,这意味着DoIndependentWork将(可能)在下载完成之前运行。 await运算符将异步等待,直到GetStringAsync返回的Task<T>完成。

但真正的问题是,如果我们有独立的工作,为什么不马上做,而不是等待等待从GetStringAsync调用? (换句话说,为什么异步方法不能按定义异步运行?)

返回方法后,异步方法的实际“工作”正在运行。 返回的Task将(通常)处于未完成的状态,这意味着该方法仍然是异步运行的。 您可以检查Task.IsCompleted以进行验证。

试试这个:

Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
Debug.WriteLine("Completed? {0}", getStringTask.IsCompleted); // Will likely print false
DoIndependentWork();
string urlContents = await getStringTask;
Debug.WriteLine("Completed now? {0}", getStringTask.IsCompleted); // Will always print true

每个async方法在每个await分成段。 每个段将成为编译器生成的状态机上的状态。

每个await指令适用于一个awaitable对于这一个Task是最常见的情况。

每个状态/段将同步执行,直到检查接收到的等待的位置为止已完成。

如果等待完成,它将继续执行下一个状态。

如果等待未完成并且没有当前的SynchronizationContext ,则执行将被阻止,直到等待完成为止,此时将开始执行下一状态。

如果存在当前的SynchronizationContext ,则执行将返回给调用者,并且当等待完成时,将继续到下一状态将被发布到捕获的SynchronizationContext

程序等待返回的字符串(如果操作没有完成则阻塞)。

await不会阻止await getStringTask 它将在编译器生成的状态机对象中保存AccessTheWebAsync方法的内部状态(局部变量和执行点所在的位置),并返回调用AccessTheWebAsync的外部方法。 状态将被恢复,并且当getStringTask任务完成后,将通过编译器生成的延续回调异步恢复执行。 如果您熟悉C#迭代器和yield关键字,则await状态机控制流与它非常相似,尽管迭代器是同步执行的。

await之后执行将如何恢复取决于启动await的线程的同步上下文 如果它是一个抽取Windows消息的UI线程,则可能会在线程消息循环的下一次迭代(通常在Application.Run )调用延续回调。 如果它是非UI线程(例如,控制台应用程序),则继续可能在不同的线程上发生。 因此,虽然您的方法的控制流仍然是逻辑线性的,但从技术上讲它不是(如果您只是执行getStringTask.Wait()而不是await getStringTask )。

程序等待返回的字符串(如果操作没有完成则阻塞)。

不,它没有。 await 等待 ,但它不会阻止任何线程。 这就是重点。

我花了一些时间来理解的一件事是GetStringAsync方法尽管它的名称同步运行(名称约定实际上是误导性的)。

那是错的。 大多数方法都是异步运行的。 它很可能在开始时也有一个小的同步部分,但这应该可以忽略不计。

在我们异步运行该方法的过程中,我们需要显式地使用Task.RunTask.Factory.StartNew

不,该方法已经异步运行。 如果你想在另一个线程上运行小同步部分,那么你可以使用Task.Run() ,但这几乎没有任何意义。 此外,不要使用StartNew() ,它不适用于异步方法。

换句话说,为什么异步方法不能按定义异步运行?

关于await的重要一点是它在它离开的相同环境中恢复。

这在GUI应用程序中非常有用,您经常需要修改UI,然后执行异步操作,然后再次修改UI。 如果async自动意味着整个方法在ThreadPool线程上执行,那么这是不可能的。

此外,这样做更有效率,因为您不必为需要很少时间的事情切换到另一个线程。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM