请看这段代码:
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
为什么?
答案 0 :(得分:5)
此代码:
await x
如果x
是一个已经完成的等待(如任务),那么该方法只是继续同步执行,就像普通方法一样。
Stephen Cleary在他的博客中提到了这一点:
“await”关键字是事物可以异步的地方。 Await就像一个一元运算符:它需要一个参数,一个等待的(“等待”是一个异步操作)。等待检查等待它是否已经完成; 如果等待已经完成,那么该方法将继续运行(同步,就像常规方法一样)。
(我的重点)
在下载的情况下,下载速度不可能太快,以至于任务在(相对)几个cpu周期内被标记为完成,直到代码到达等待。
但是,在异步写入中,甚至可能存在一个优化,即只会同步完成小写操作,因为异步的开销可能使实际写入相形见绌,或者您可能会遇到写操作在代码试图等待它之前它完成的时间很短。
一个必然结果是你应该使用await Task.Delay(10000);
而不是Thread.Sleep(10000);
,因为前者会更多"异步友好"。
答案 1 :(得分:1)
发生了什么StreamWriter
已经完成,运行时可以检查它是否已完成并将控制权返回给主线程。在这种情况下,异步方法不会阻止,它是Thread.Sleep(10000)
调用。要解决此问题,请尝试使用await Task.Delay(10000)
。这应该产生您正在寻找的异步代码。