【问题标题】:Why does this async method quit before writing to file?为什么这个异步方法在写入文件之前退出?
【发布时间】:2015-02-12 18:20:30
【问题描述】:

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

没有任何东西被写入文件。我在这里做错了什么?

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

【问题讨论】:

  • 当您使用调试器时,result 是否有值?如果确实如此,则意味着您的 WriteToFile 方法没有做它应该做的事情。你有什么例外吗?
  • 没有例外。主线程在代码可以完成工作之前完成,如 m1o2 的回答。
  • 您确认了吗?因为当我执行您的代码并打印 result 时,它会给我正确的行为。
  • 正如你提到的,我能够打印结果,但前提是我在主线程上使用Task.Wait()
  • async/await 的全部意义在于让您编写看起来同步但实际上是非阻塞的代码。因此,您在 Main 中对 AsyncExample 的调用几乎立即返回(在第一次等待时),并将继续在后台线程上执行其业务(广义而言)。然后,Main 函数本身返回,无需等待结果。

标签: c# .net task-parallel-library async-await


【解决方案1】:

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

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

要解决这个问题,您需要等到 AsyncExample 完成。

一种方法是使用延续,然后使用Task.Wait()方法等待这个延续任务。

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

但是,如果我们不使用延续,并且我们没有从错误任务中读取异常值或结果,那么在完成任务时,应用程序可能会终止。延续处理这两种情况的技巧。

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

【讨论】:

  • 就是这样。这行得通。我只是使用 task.Wait(); 而不是 task.ContinueWith( t =&gt; { }).Wait(); 并且它有效。
  • @Nanda 好的,没关系,只要您记住,如果您在出现错误的情况下等待原始任务而不继续,则可能会收到未处理的异常。
  • 当然,我会记住这一点并研究延续机制。
  • 除非我遗漏了一些东西,否则在这里搞乱延续是不必要的。任务的想法是您使用 async/await 或 ContinueWith 组合它们,因此它们保持非阻塞;当你想得到最终结果时,你调用 Wait 就可以了。这就是你会得到任务产生的任何异常的地方,如果有的话。建议在这里添加一个延续来抑制错误就像建议使用一个包罗万象的处理程序将方法调用包装在一个 try 块中。极少情况下,您想抑制所有异常……但是,通常您想了解它们。
  • @pkt 我认为你是对的。我编辑了我的答案。谢谢。
【解决方案2】:

对于 m1o2 的回答,有些东西太长了,无法评论:

但是,如果我们不使用延续,并且我们没有从错误任务中读取异常值或结果,那么在完成任务时,应用程序可能会终止。

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

  2. 无需继续。当使用await task 时,编译器会将我们的代码提升到状态机中,该状态机稍后将运行await 关键字之后的任何代码作为“延续”。如果等待的任务内部发生异常,一旦任务执行完毕,就会抛出异常。

  3. 访问Task.Wait 将传播AggregationException,您不必访问Task.ResultTask.Exception。拍摄from MSDN

由在任务中运行的用户代码引发的未处理异常会传播回加入线程,本主题后面描述的某些情况除外。当您使用静态或实例 Task.WaitTask&lt;TResult&gt;.Wait 方法之一并通过将调用包含在 try-catch 语句中来处理它们时,会传播异常。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-29
    • 2015-08-27
    • 2018-12-03
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 2015-08-08
    • 1970-01-01
    相关资源
    最近更新 更多