2013-03-12 25 views
32

私は、System.Net.HttpClientを短いタイムアウトで使用しているときに、try-catchブロックでラップされていてもプロセスをクラッシュさせることがあることに気付きました。これを再現するための短いプログラムがあります。HttpClientを短すぎるタイムアウトに設定するプロセス

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

次の例外を除いて、プロセスがクラッシュします。この実行:

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

少しで掘り、HttpClientは、関連ServicePoint前に要求を中止する場合、作成するHttpWebRequest試みを作成しているようですServicePointServicePointManager.FindServicePoint経由で、RequestCanceledをスローします。この例外は、要求を取り消そうとしているスレッドでスローされるため、キャッチされず、プロセスは終了します。

は、私は何かが足りないのですか?あなたはこの問題にぶつかりましたか?

+0

これはすばらしいバグです。 a)メイン全体でtry-catchを追加できますか? b)TaskScheduler.UnobservedTaskExceptionイベントイベントをフックできますか? – usr

+0

@usr:両方とも試してみましたが、期待通りに役に立たなかった。メインのtry-catchはメインスレッドのものだけをキャッチし、プロセスクラッシュの例外はUnobservedTaskExceptionではありません。 –

+0

aggregateExceptionをキャッチできませんか?そこからそれを扱う? キャッチ(AggregateExceptionのAE) {} – PaulG

答えて

20

HttpWebRequest.Abort()背景/タイマースレッドに例外をスローしています。これは、HttpClientのタスク管理とは関係ありません。

HttpWebRequest.Abort()の例外は、.NET 4.5 GDR1で修正する必要があります。 http://support.microsoft.com/kb/2750149 http://support.microsoft.com/kb/2750147

+2

間違いなくパッチの問題。私はこれが私の最新のパッチを当てたWindows 8のパッチを適用したラップトップでうまく動作することを確認できますが、企業のアップデートからパッチを受け取る私の仕事用ラップトップでは動作しません。 –

+1

アップデートをインストールできない場合のオプションはありますか? (おそらく古いHttpWebRequestに戻るのだろうか?) –

4

HttpClientの非同期ハンドラがタスクをどのように管理しているかは、何らかのバグです。私は並行してアイテムを起動することができましたが、それらを同期して実行すると動作します。私はあなたが未処理のエラーを防ぐかどうか分からない。これは並列タスクを実行しましたが、私はそれをオフにして以来、実際には非同期ではありません。私のコンピュータでは、私はいつも5ラウンドになるだろう、そして、それはクラッシュするだろう。私が1秒後にタイムアウトするように設定したとしても、非同期であればスレッドのクラッシュが依然として爆発するようなものです。

私はバグだと思いますが、これは意図された動作であるとは想像できません。

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

HttpWebRequest(HttpClientが送信中に使用する)にバグがあるようです。 Tratcherの答えを見てください。 –

+0

実際にParallel.ForEachを非同期で使用するべきではありません。ここにはモンスターがいる。 – Matt

関連する問題