为什么这个异步方法在写入文件之前退出

本文关键字:文件 退出 异步方法 为什么 | 更新日期: 2023-09-27 18:10:47

我正试图了解async/await,并编写了以下控制台应用程序来下载给定URL的页面内容(基于dotnetperls的httpclient示例(

文件中不会写入任何内容。我在这里做错了什么?

static void Main()
{
    AsyncExample();
}
static async void AsyncExample()
{
    Task<string> task = DownloadPageAsync();
    string result = await task;
    WriteToFile(result, someFileName);
}
static async Task<string> DownloadPageAsync()
{
    string page = "http://en.wikipedia.org/wiki/Asynchronous_I/O";
    string result = string.Empty;
    using(HttpClient client = new HttpClient())
    using(HttpResponseMessage response = await client.GetAsync(page))
    using(HttpContent content = response.Content)
    {
        result = await content.ReadAsStringAsync();
    }
    return result;
}

为什么这个异步方法在写入文件之前退出

这是因为应用程序已终止。您需要等到AsyncExample完成后,才能让main完成它的代码。

目前,Main方法在任务完成之前完成,因此无论任务状态如何,应用程序都会立即终止。

要解决此问题,您需要等待AsyncExample完成。

一种方法是

使用continuation,然后使用Task等待该continuationTask。Wait((方法。

我们使用continuation来处理错误。从原始任务中读取Result属性将在出错时引发异常(如果希望在此处引发异常,请执行此操作(

但是,如果我们没有使用continuation,并且没有从出现故障的任务中读取Exception值或Result,那么在完成task时,应用程序可能会终止。continuation的技巧处理了这两种情况。

static async Task AsyncExample()
{
    Task<string> task = DownloadPageAsync();
    string result = await task;
    WriteToFile(result, someFileName);
}
public static void Main(string[] args)
{
    var task = AsyncExample();
    task.Wait();
    // task.ContinueWith( t => { }).Wait();
}

对于m1o2的答案来说,有几件事太长了:

但是,如果我们没有使用continuation,并且没有从出现故障的任务中读取Exception值或Result,那么在完成task时,应用程序可能会终止。

  1. TPL中未处理异常的行为方式发生了突破性的变化。在里面NET 4.0,UnobservedTaskException将终止您的进程。在里面NET 4.5,则不会。有关更多信息,请参阅Stephan Toub 的this blog post

  2. 没有必要继续下去。当使用await task时,编译器将我们的代码提升到一个状态机中,该状态机稍后将运行await关键字之后的任何代码作为"continuation"。如果等待的任务内部发生异常,则在任务完成执行后将抛出该异常。

  3. 访问Task.Wait将产生AggregationException,您不必访问Task.ResultTask.Exception。拍摄from MSDN:

由任务内部运行的用户代码引发的未处理异常将传播回加入线程,本主题稍后将描述的某些场景除外。当您使用静态或实例Task.WaitTask<TResult>.Wait方法之一时,会传播异常,并通过将调用封装在try-catch语句中来处理它们。