为什么有些异步方法是同步执行的

时间:2016-05-10 19:45:53

标签: c# asynchronous async-await

请看这段代码:

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();
    }

}

根据async / await的逻辑,在这种情况下一切正常。

输出:

  • MainEnd

  • 10秒后

  • DoStuffEnd

但如果我评论(1)并取消注释(2),则输出为:

  • 10秒后

  • DoStuffEnd

  • MainEnd

为什么?

2 个答案:

答案 0 :(得分:5)

此代码:

await x

如果x是一个已经完成的等待(如任务),那么该方法只是继续同步执行,就像普通方法一样。

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

Async and Await

  

“await”关键字是事物可以异步的地方。 Await就像一个一元运算符:它需要一个参数,一个等待的(“等待”是一个异步操作)。等待检查等待它是否已经完成; 如果等待已经完成,那么该方法将继续运行(同步,就像常规方法一样)。

(我的重点)

在下载的情况下,下载速度不可能太快,以至于任务在(相对)几个cpu周期内被标记为完成,直到代码到达等待。

但是,在异步写入中,甚至可能存在一个优化,即只会同步完成小写操作,因为异步的开销可能使实际写入相形见绌,或者您可能会遇到写操作在代码试图等待它之前它完成的时间很短。

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

答案 1 :(得分:1)

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