2013-03-12 6 views
32

Ich habe festgestellt, dass wenn ich System.Net.HttpClient mit einem kurzen Timeout verwenden, kann es manchmal den Prozess abstürzen, auch wenn es in einem try-catch-Block verpackt ist. Hier ist ein kurzes Programm, um dies zu reproduzieren.Einstellung von HttpClient auf einen zu kurzen Timeout stürzt Prozess

public static void Main(string[] args) 
{ 
    var tasks = new List<Task>(); 
    for (int i = 0; i < 1000; i++) 
    { 
     tasks.Add(MakeHttpClientRequest()); 
    } 
    Task.WaitAll(tasks.ToArray()); 

} 

private async static Task MakeHttpClientRequest() 
{    
    var httpClient = new HttpClient { Timeout = TimeSpan.FromMilliseconds(1) }; 
    var request = "whatever"; 
    try 
    { 
     HttpResponseMessage result = 
      await httpClient.PostAsync("http://www.flickr.com/services/rest/?method=flickr.test.echo&format=json&api_key=766c0ac7802d55314fa980727f747710", 
           new StringContent(request));    
     await result.Content.ReadAsStringAsync();     
    } 
    catch (Exception x) 
    { 
     Console.WriteLine("Error occurred but it is swallowed: " + x); 
    } 
} 

das Lauf wird der Prozess mit folgender Ausnahme abstürzen:

Unhandled Exception: System.AggregateException: One or more errors occurred. ---> System.Net.WebException: The request was canceled 
    at System.Net.ServicePointManager.FindServicePoint(Uri address, IWebProxy proxy, ProxyChain& chain, HttpAbortDelegate& abortDelegate, Int32& abortState) 
    at System.Net.HttpWebRequest.FindServicePoint(Boolean forceFind) 
    at System.Net.HttpWebRequest.get_ServicePoint() 
    at System.Net.AuthenticationState.PrepareState(HttpWebRequest httpWebRequest) 
    at System.Net.AuthenticationState.ClearSession(HttpWebRequest httpWebRequest) 
    at System.Net.HttpWebRequest.ClearAuthenticatedConnectionResources() 
    at System.Net.HttpWebRequest.Abort(Exception exception, Int32 abortState) 
    at System.Net.HttpWebRequest.Abort() 
    at System.Net.Http.HttpClientHandler.OnCancel(Object state) 
    at System.Threading.CancellationCallbackInfo.ExecutionContextCallback(Object obj) 
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
    at System.Threading.CancellationCallbackInfo.ExecuteCallback() 
    at System.Threading.CancellationTokenSource.CancellationCallbackCoreWork(CancellationCallbackCoreWorkArguments args) 
    at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException) 
    --- End of inner exception stack trace --- 
    at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException) 
    at System.Threading.CancellationTokenSource.NotifyCancellation(Boolean throwOnFirstException) 
    at System.Threading.CancellationTokenSource.TimerCallbackLogic(Object obj) 
    at System.Threading.TimerQueueTimer.CallCallbackInContext(Object state) 
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.TimerQueueTimer.CallCallback() 
    at System.Threading.TimerQueueTimer.Fire() 
    at System.Threading.TimerQueue.FireNextTimers() 
    at System.Threading.TimerQueue.AppDomainTimerCallback() 

in einem kleinen Graben, so scheint es, dass, wenn HttpClient die Anforderung bricht ab, bevor ein relevantes ServicePoint erstellt wird, HttpWebRequest versucht das zu schaffen ServicePoint, über ServicePointManager.FindServicePoint, die eine RequestCanceled wirft. Da diese Ausnahme in dem Thread ausgelöst wird, der versucht, die Anforderung abzubrechen, wird sie nicht abgefangen und der Prozess wird beendet.

Fehle ich etwas? Bist du auf dieses Problem gestoßen?

+0

Dies ist ein erstaunlicher Fehler. a) Können Sie versuchen, einen Try-Catch um den gesamten Main hinzuzufügen? b) Können Sie das Ereignis TaskScheduler.UnobservedTaskException Event haken? – usr

+0

@usr: versuchte beide, und wie erwartet hat es nicht geholfen. Try-catch on main fängt nur Hauptthreads ab, und die prozessauslösende Ausnahme ist keine UnobservedTaskException. –

+0

Konnten Sie die aggregateException nicht abfangen? Und damit von dort umgehen? catch (Aggregate ae) { } – PaulG

Antwort

20

HttpWebRequest.Abort() wird eine Ausnahme auf einem Hintergrund/Timer-Thread zu werfen. Dies hat nichts mit der Aufgabenverwaltung von HttpClient zu tun.

Die Ausnahme von HttpWebRequest.Abort() sollte in .NET 4.5 GDR1 behoben werden. http://support.microsoft.com/kb/2750149 http://support.microsoft.com/kb/2750147

+2

Definitiv ein Patch-Problem. Ich kann bestätigen, dass dies auf meinem aktuell gepatchten gepatchten Windows 8-Laptop funktioniert, funktioniert aber nicht auf meinem Arbeitslaptop, der Patches von Unternehmensupdates erhält. –

+1

Alle Optionen, wenn Sie das Update nicht installieren können? (Gehen Sie vielleicht zurück zum alten HttpWebRequest?) –

4

Es sieht so aus, als wäre es ein Fehler, wie der Async-Handler für HttpClient die Aufgaben verwaltet. Ich war in der Lage, die Elemente parallel zu starten, aber sie synchron laufen und es funktioniert. Ich bin mir nicht sicher, ob Sie den unbehandelten Fehler einfach verhindern wollen oder nicht. Das hat parallele Aufgaben ausgeführt, aber sie sind nicht wirklich asynchron, seit ich sie ausgeschaltet habe. Auf meinem Computer würde ich immer zu 5 Runden kommen und es würde abstürzen. Auch wenn ich es nach einer Sekunde auf Timeout setze, ist es so, als würden die Abstürze in den Threads immer noch explodieren, wenn sie asynchron wären.

Ich denke, es ist ein Fehler, ich kann mir nicht vorstellen, dass dies beabsichtigt ist.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Net.Http; 

namespace TestCrash 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      try 
      { 
       Parallel.ForEach(Enumerable.Range(1, 1000).ToList(), i => 
       { 
        Console.WriteLine(i); 
        using (var c = new HttpClient { Timeout = TimeSpan.FromMilliseconds(1) }) 
        { 
         var t = c.GetAsync("http://microsoft.com"); 
         t.RunSynchronously(); //<--comment this line and it crashes 
         Console.WriteLine(t.Result); 
        } 
       }); 
      } 
      catch (Exception x) 
      { 
       Console.WriteLine(x.Message); 
      } 
      Console.ReadKey(); 
     } 
    } 
} 
+1

auftreten kann. Scheint, es gibt einen Fehler in HttpWebRequest (die HttpClient in seinem Senden verwendet). Siehe Tratcher's Antwort. –

+0

Sollte nicht wirklich Parallel.ForEach mit Async verwenden. Hier sind Monster. – Matt