C# HttpClient在GetAsync上使用Wait时未引发异常

C# HttpClient在GetAsync上使用Wait时未引发异常,c#,task-parallel-library,async-await,C#,Task Parallel Library,Async Await,我使用以下代码获取端点并将其写入缓存: public async Task UpdateCacheFromHttp(string Uri) { if (string.IsNullOrEmpty(Uri)) return; var httpClient = new HttpClient(); var response = await httpClient.GetAsync(Uri); if ((response != null) &&am

我使用以下代码获取端点并将其写入缓存:

public async Task UpdateCacheFromHttp(string Uri)
{
    if (string.IsNullOrEmpty(Uri))
        return;

    var httpClient = new HttpClient();
    var response = await httpClient.GetAsync(Uri);

    if ((response != null) && (response.IsSuccessStatusCode))
    {
        var responseStream = await response.Content.ReadAsStreamAsync();
        WriteToCache(responseStream);
    }
}
代码正在IIS上运行

如果无法到达端点,我希望GetAsync抛出异常。即使是试一试,它似乎永远不会失败。GetAsync从未返回(我在HttpClient上尝试了5秒超时,但仍然没有返回)

这会引发异常:

public Task UpdateCacheFromHttp(string Uri)
{
    var updateCacheTask = Task.Factory.StartNew(new Action(() =>
    {
        if (string.IsNullOrEmpty(Uri))
            return;

        var httpClient = new HttpClient();
        var response = httpClient.GetAsync(Uri).Result;

        if (response.IsSuccessStatusCode)
        {
            var responseStream = response.Content.ReadAsStreamAsync().Result;
            WriteToCache(responseStream);
        }
    }));

    return updateCacheTask;
}
我得到了预期的“无法连接到远程服务器”


我怀疑这与IIS中运行的代码有关,但为什么?如何让它在不需要启动新任务的情况下正确地抛出异常?

我的直觉告诉我,您正在调用
Wait
Result
进一步调用堆栈


如果这是正确的,那么您造成了死锁,因为。

由于我遇到了相同的行为,没有引发异常,因此我创建了一个示例来演示问题和可能的解决方案:

using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

namespace Exam
{
    public static class Program
    {
        private static async Task<string> GetWebPage(string uri)
        {
            var httpClient = new HttpClient();
            var response = await httpClient.GetAsync(new Uri(uri, UriKind.Absolute), HttpCompletionOption.ResponseContentRead);
            return await response.Content.ReadAsStringAsync();
        }

        public static void Main(string[] args)
        {
            try
            {
                // These two lines do not work - i.e. it terminates the application without any exception being thrown...
                //string s = await GetWebPage(@"https://www.dwd.de/DE/leistungen/klimadatendeutschland/klimadatendeutschland.html");
                //Console.WriteLine(s);

                // This works:
                Task<string> getPageTask = GetWebPage(@"https://www.dwd.de/DE/leistungen/klimadatendeutschland/klimadatendeutschland.html");
                getPageTask.Wait();
                if (getPageTask.IsCompleted)
                    Console.WriteLine(getPageTask.Result);
            }
            catch (AggregateException aex)
            {
                aex.InnerExceptions.AsParallel().ForAll(ex => Console.WriteLine(ex));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            Console.ReadKey();
        }
    }
}
使用系统;
使用System.Linq;
使用System.Net.Http;
使用System.Threading.Tasks;
名称空间检查
{
公共静态类程序
{
私有静态异步任务GetWebPage(字符串uri)
{
var httpClient=新的httpClient();
var response=wait-httpClient.GetAsync(新Uri(Uri,UriKind.Absolute),HttpCompletionOption.ResponseContentRead);
return wait response.Content.ReadAsStringAsync();
}
公共静态void Main(字符串[]args)
{
尝试
{
//这两行不起作用-即它终止应用程序而不引发任何异常。。。
//字符串s=等待获取网页(@)https://www.dwd.de/DE/leistungen/klimadatendeutschland/klimadatendeutschland.html");
//控制台。写入线(s);
//这项工作:
任务getPageTask=GetWebPage(@)https://www.dwd.de/DE/leistungen/klimadatendeutschland/klimadatendeutschland.html");
getPageTask.Wait();
如果(getPageTask.IsCompleted)
Console.WriteLine(getPageTask.Result);
}
捕获(聚合异常aex)
{
ForAll(ex=>Console.WriteLine(ex));
}
捕获(例外情况除外)
{
控制台写入线(ex);
}
Console.ReadKey();
}
}
}
当您另外将URI更改为@“无效”之类的内容时https://....“您将检索AggregateException。
希望,它对任何人都有帮助:-)

这是一种直觉。谢谢。取消注释行
string s=await GetWebPage(…
应该会导致编译时错误,因为调用方法不是异步的。也就是说,我非常怀疑它是否会导致应用程序在没有引发任何异常的情况下终止。嘿,Theodor,如果您添加了“async”,它会按照说明工作。我需要删除“async”为了不获取编译错误,在注释两个标记行时;-)Cordt如果将
async
添加到
void Main
中,则会得到一个
async void
,即。异步任务的正确签名是
async Task Main()
.Theodor,感谢您的澄清。我知道异步void是要避免的,因为它不转发异常。我遇到这个问题是因为当我为C#考试做准备时,我无一例外地遇到了课程的终止。所以我在谷歌上找到了这条线索。我不知道发生这种情况的确切原因,但现在同一段代码正在无终止地运行。我只能猜测加载页面的时间比今天要长,这就是为什么它能工作的原因。是的,这是一个合理的解释。顺便说一句,
async void
方法不像fire-and-forget任务那样无法观察到异常。
async void
引发的异常将在捕获的
synchronizationcontext
上重新引发,或者在
ThreadPool
上(如果它们不存在),因此您肯定会观察到它(进程将崩溃)。由于这个原因,
async void
s有时被称为fire and crash。