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

C# 为什么这个异步方法在写入文件之前退出?,c#,.net,task-parallel-library,async-await,C#,.net,Task Parallel Library,Async Await,我正在尝试了解async/await,并编写了以下控制台应用程序来下载给定URL的页面内容(基于来自dotnetperls的) 没有任何内容写入该文件。我做错了什么 static void Main() { AsyncExample(); } static async void AsyncExample() { Task<string> task = DownloadPageAsync(); string result = await task; W

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

没有任何内容写入该文件。我做错了什么

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;
}
static void Main()
{
异步示例();
}
静态异步void AsyncExample()
{
任务任务=下载页面异步();
字符串结果=等待任务;
WriteToFile(结果,文件名);
}
静态异步任务下载页面异步()
{
字符串页=”http://en.wikipedia.org/wiki/Asynchronous_I/O";
字符串结果=string.Empty;
使用(HttpClient=new HttpClient())
使用(httpresponsemessageresponse=wait client.GetAsync(第页))
使用(HttpContent=response.content)
{
结果=等待内容。ReadAsStringAsync();
}
返回结果;
}

这是因为应用程序已终止。您需要等到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();
}
静态异步任务AsyncExample()
{
任务任务=下载页面异步();
字符串结果=等待任务;
WriteToFile(结果,文件名);
}
公共静态void Main(字符串[]args)
{
var task=AsyncExample();
task.Wait();
//task.ContinueWith(t=>{}).Wait();
}

对于m1o2的回答,有几点评论太长了:

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

  • TPL中未处理异常的行为方式发生了重大变化。在.NET4.0中,一个
    未观察到的taskeexception
    将终止您的进程。在.NET 4.5中,它将不会。有关这方面的更多信息,请参见Stephan Toub

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

  • 访问
    Task.Wait
    将产生
    AggregationException
    ,您不必访问
    Task.Result
    Task.Exception
    。采取:

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


    使用调试器时,
    result
    是否有值?如果是这样,则表示您的
    WriteToFile
    方法没有执行它应该执行的操作。你有例外吗?没有例外。主线程在代码完成其工作之前完成,如m1o2的回答所示。您确认了吗?因为当我执行代码并只打印
    result
    时,它会给出正确的行为。正如您所提到的,我能够打印结果,但前提是我在主线程上使用
    Task.Wait()
    。async/Wait的整个要点是让您编写看起来是同步的,但实际上是非阻塞的代码。因此,在Main中调用AsyncExample几乎立即返回(在第一次等待时),并将继续在后台线程上执行其业务(广义地说)。然后,主函数本身返回,而不等待结果。就是这样。这起作用了。我刚才用了
    task.Wait()而不是
    task.ContinueWith(t=>{}).Wait()
    成功了。@Nanda好的,只要你记住,如果你在原始任务中等待,并且在出现错误时没有继续,那么你可能会得到一个未处理的异常。当然,我会记住这一点,并研究继续机制。除非我遗漏了什么,否则在这里不必再处理继续。任务的思想是使用async/Wait或ContinueWith组合它们,因此它们保持非阻塞状态;当你想得到最后的结果时,你可以调用Wait。这就是你将得到任务产生的任何异常的地方,如果有的话。建议在这里添加一个continuation来抑制错误就像建议在一个带有catch-all处理程序的try块中包装方法调用一样。很少情况下,您希望抑制所有异常。。。不过,通常你都想了解他们。@pkt我认为你是对的。我编辑了我的答案。谢谢