为什么某些异步方法同步执行



请看这个代码:

class Program
{
    static async void DoStuff()
    {
        StreamWriter s = new StreamWriter("a.txt");
        WebClient wc = new WebClient();
        await wc.DownloadStringTaskAsync(new Uri("http://www.microsoft.com"));  //1
        //await s.WriteAsync ("123");                                           //2
        Thread.Sleep(10000);
        Console.WriteLine("DoStuffEnd");
    }
    static void Main(string[] args)
    {
        Program.DoStuff();
        Console.WriteLine("MainEnd");
        Console.Read();
    }
}

在这种情况下,根据异步/等待的逻辑,一切都很好。

输出:

  • 主端

  • 10 秒后

  • 做东西结束

但是如果我评论 (1( 和取消评论 (2(,输出是:

  • 10 秒后

  • 做东西结束

  • 主端

为什么?

此代码:

await x

如果x是已完成的可等待(如任务(,则该方法会像普通方法一样继续同步执行。

Stephen Cleary在他的博客上提到了这一点:

异步和等待:

"await"关键字是事情可以异步的地方。Await 就像一个一元运算符:它接受一个参数,一个可等待的("awaitable"是一个异步操作(。等待检查等待,看看它是否已经完成;如果 Awaitable 已经完成,则该方法将继续运行(同步,就像常规方法一样(。

(我的强调(

在下载

的情况下,下载不太可能完成得如此之快,以至于任务在(相对(几个 CPU 周期内被标记为完成,直到代码到达 await。

但是,在异步写入

中,甚至可以进行优化,说明某某小写入将同步完成,因为异步的开销可能会使实际写入相形见绌,或者您可能会遇到写入花费的时间太短,以至于在代码尝试等待它之前完成。

一个推论是你应该使用await Task.Delay(10000);而不是Thread.Sleep(10000);因为前者会更"异步友好"。

正在发生的事情是,在运行时可以检查它是否已完成并将控制权返回给主线程之前,StreamWriter已经完成。 在这种情况下,异步方法不是阻塞,而是Thread.Sleep(10000)调用。 要解决此问题,请尝试改为await Task.Delay(10000)。 这应该会生成您要查找的异步代码。

最新更新