请看这个代码:
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)
。 这应该会生成您要查找的异步代码。