C# 我与HttpClient的GET请求循环陷入了一系列神秘的AggregateException

C# 我与HttpClient的GET请求循环陷入了一系列神秘的AggregateException,c#,multithreading,http,C#,Multithreading,Http,我不熟悉这种语言,也不熟悉HttpClient,而且不擅长多线程,所以请尽量容忍我。我有一个Windows窗体应用程序,允许用户输入ID。当用户按开始时,它将开始重复向某个网站的web API发出请求(直到他们按停止),每次ID递增1。该ID包含在请求中;它使站点返回一个与该ID关联的虚拟对象的JSON表。每分钟有数千个这样的对象上传到站点 以下是我用于发出请求的方法: private async Task<string> GetResponseText(HttpClient cli

我不熟悉这种语言,也不熟悉HttpClient,而且不擅长多线程,所以请尽量容忍我。我有一个Windows窗体应用程序,允许用户输入ID。当用户按开始时,它将开始重复向某个网站的web API发出请求(直到他们按停止),每次ID递增1。该ID包含在请求中;它使站点返回一个与该ID关联的虚拟对象的JSON表。每分钟有数千个这样的对象上传到站点

以下是我用于发出请求的方法:

private async Task<string> GetResponseText(HttpClient client, string address)
{
        return await client.GetStringAsync(address);
}
然后我创建一个全新的线程并开始循环:

System.Threading.Thread scanThread = new System.Threading.Thread(() =>
{
    while (scanning)
    {
        string assetUrl = "https://api.example.com/blah?id=" + assetId.ToString();
        string response = GetResponseText(client, assetUrl).Result;
        dynamic assetJson = JsonConvert.DeserializeObject(response);
        if (assetJson != null)
        {
            this.Invoke((MethodInvoker)delegate
            {
                try
                {
                    // omitted lots of irrelevant stuff
                    assetId += 1;
                }
                catch (RuntimeBinderException)
                {
                    // this probably means the ID doesn't exist yet so I do nothing
                }
            });
        }
    }
    System.Threading.Thread.CurrentThread.Abort();
});
scanThread.IsBackground = true;
scanThread.Start();
我第一次运行它时,它运行得很好。每一个ID都成功了,每秒至少有5到10个请求。在幕后没有奇怪的例外或问题或类似的事情。但是,出乎意料的是,在一两次请求之后,我遇到了第一个问题:

A first chance exception of type 'System.Net.Http.HttpRequestException' occurred in mscorlib.dll
A first chance exception of type 'System.AggregateException' occurred in mscorlib.dll
An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll
Additional information: One or more errors occurred.
堆栈跟踪将问题缩小到这一行:

string response = GetResponseText(client, assetUrl).Result;
“没关系,”我心里想。我不知道出了什么问题,所以我为那条线设置了一个try/catch块。这才是真正让我抓狂的部分

有了try/catch,一旦这个错误出现,整个循环就会一遍又一遍地发生。它永远不会恢复或继续正常。有两种方法可以解决这个问题,第二种是最奇怪的:

1) 只需关闭程序并再次打开即可

2) 停止扫描,耐心等待Visual Studio的调试输出中出现大约6行关于线程退出的内容,然后再次开始扫描


我觉得第二种情况是关于为什么会发生这种情况的线索,但我还没有弄清楚。发生了什么事?为什么等待一些线程关闭(不管它们是从什么开始的)可以解决这个问题呢?每次循环运行时,我是否可以手动删除这些内容?

HTTP状态码400表示您的请求无效。因此,您必须详细检查您的HTTP请求。也许API只允许有限数量的访问或其他

此外,我将为您提供一些更现代的代码示例:

   var ts = new CancellationTokenSource();
   CancellationToken ct = ts.Token;

    private void Start()
    {
       var task = Task.Factory.StartNew(() => {

         var client = new HttpClient();
         while(true)
         {
            if (ct.IsCancellationRequested)
            {
               break;
            }

            string assetUrl = "https://api.example.com/blah?id=" + assetId;
            string response = await GetResponseText(client, assetUrl);
            dynamic assetJson = JsonConvert.DeserializeObject(response);

            if (assetJson != null)
            {
               assetId++;
            }
         }
       }, ct).ContinueWith(ex => Console.WriteLine(ex.Message), TaskContinuationOptions.OnlyOnFaulted);
     }


     private void Stop()
     {
        ct.Cancel();
     }

请使用主线程尝试相同的方法。因此,删除您的线程并直接在表单中传递while循环。现在在循环的末尾添加Application.DoEvents();因此,您可以做出反应并单击“停止”按钮或其他。这不是解决方案,但如果它现在对您有帮助,则只是您的线程导致了此问题。深入研究AggregateExceptions并找出内部异常是什么are@SebiApplication.DoEvents()停止了整个应用程序的响应,即使在某些情况下放置在while循环中reason@AntP我不知道该怎么办that@Autumn捕获异常并检查
InnerExceptions
属性时,请在中放置断点。谢谢。我非常确定问题出在这个特定的API上,我将在下次可能的时候研究它。现代的代码对您很有帮助,欢迎光临。是的,我认为api也会成为问题。
   var ts = new CancellationTokenSource();
   CancellationToken ct = ts.Token;

    private void Start()
    {
       var task = Task.Factory.StartNew(() => {

         var client = new HttpClient();
         while(true)
         {
            if (ct.IsCancellationRequested)
            {
               break;
            }

            string assetUrl = "https://api.example.com/blah?id=" + assetId;
            string response = await GetResponseText(client, assetUrl);
            dynamic assetJson = JsonConvert.DeserializeObject(response);

            if (assetJson != null)
            {
               assetId++;
            }
         }
       }, ct).ContinueWith(ex => Console.WriteLine(ex.Message), TaskContinuationOptions.OnlyOnFaulted);
     }


     private void Stop()
     {
        ct.Cancel();
     }