2013-03-12 16 views
32

Zauważyłem, że kiedy używam System.Net.HttpClient z krótkim czasem oczekiwania, może to czasami spowodować awarię procesu, nawet jeśli jest zapakowany w blok try-catch. Oto krótki program do odtworzenia tego.Ustawienie HttpClient na zbyt krótki czas oczekiwania awarie

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

Running to padnie proces z następującym wyjątkiem:

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

kopanie trochę, wydaje się, że gdy HttpClient przerywa wniosku przed odpowiednim ServicePoint jest tworzony, HttpWebRequest prób, aby utworzyć ServicePoint, przez ServicePointManager.FindServicePoint, który zgłasza RequestCanceled. Ponieważ ten wyjątek jest zgłaszany w wątku, który próbuje anulować żądanie, nie jest przechwytywany, a proces umiera.

Czy brakuje mi czegoś? Czy napotkasz ten problem?

+0

To jest niesamowity błąd. a) Czy możesz spróbować dodać try-catch w całym Main? b) Czy możesz podłączyć wydarzenie EventScheduler.UnobservedTaskException? – usr

+0

@usr: wypróbowałem oba i zgodnie z oczekiwaniami to nie pomogło. Try-catch na main przejmuje główne wątki, a wyjątek powodujący zawieszenie procesu nie jest wyjątkiem UnobservedTaskException. –

+0

Nie możesz złapać wyjątku aggregateException? I stamtąd sobie z tym poradzić? zaczep (AggregateException AE) { } – PaulG

Odpowiedz

20

HttpWebRequest.Abort() rzuca wyjątek w wątku/e czasowego. Nie ma to nic wspólnego z zarządzaniem zadaniami HttpClient.

Wyjątek od HttpWebRequest.Abort() należy naprawić w .NET 4.5 GDR1. http://support.microsoft.com/kb/2750149 http://support.microsoft.com/kb/2750147

+2

Zdecydowanie problem z łatką. Mogę potwierdzić, że działa to dobrze na moim uaktualnionym łatanym laptopie z Windows 8, ale nie działa na moim laptopie roboczym, który otrzymuje łatki z aktualizacji korporacyjnych. –

+1

Jakieś opcje, jeśli nie możesz zainstalować aktualizacji? (może wrócić do starego HttpWebRequest?) –

4

Wygląda na to, że jest to jakiś błąd w zarządzaniu zadaniami przez moduł obsługi asynchronicznej dla HttpClient. Udało mi się uruchomić elementy równolegle, ale uruchom je synchronicznie i działa. Nie jestem pewien, czy chcesz po prostu zapobiec nieobsługiwanemu błędowi, czy nie. To działało równolegle do zadań, ale nie są one tak mocno powiązane, odkąd je wyłączyłem. Na moim komputerze zawsze dostałem się do 5 rund, a to by się zawiesiło. Nawet jeśli ustawię go na timeout po chwili, to tak, jakby awarie w wątkach nadal byłyby wybuchające, gdyby były asynchroniczne.

Myślę, że to błąd, nie mogę sobie wyobrazić, że jest to zamierzone zachowanie.

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

Wydaje się, że jest błąd w HttpWebRequest (którego HttpClient używa w wysyłaniu). Zobacz odpowiedź Tratchera. –

+0

Nie powinien naprawdę używać Parallel.ForEach z asynchronizacją. Oto potwory. – Matt

Powiązane problemy