2014-07-17 5 views
2

여러 스레드에서 HttpClient를 사용하는 데 약간의 문제가 있습니다. 나는 하나 개의 스레드가있는 경우 내가 동시에 많은 다운로드를 시작하면.NET HttpClient multithreading

는 각 스레드의 첫 번째 다운로드가 매우 느립니다 (및 병렬 스레드 증가) 예를 들어

, 모든 것이 괜찮

First download Elapsed Time Download: 197 ms 
Second download Elapsed Time Download: 171 ms 

하지만 하나의 스레드와 다운로드 시간 증가

First download Elapsed Time Download: 3881 ms 
... 
Second download Elapsed Time Download: 96 ms 
... 

네트워크 대역폭은 문제가되지 않습니다, 나는 localhost와 동일한 문제가 있습니다.

static void Main(string[] args) 
{ 
    ServicePointManager.DefaultConnectionLimit = 200; 
    List<Task> tasks = new List<Task>(); 
    for (var i = 0; i < 10; i++) 
    { 
     tasks.Add(
      Task.Factory.StartNew(() => 
      { 
       Stopwatch s = Stopwatch.StartNew(); 
       HttpClient httpClient = new HttpClient(); 
       HttpResponseMessage httpDownloadResponse = httpDownloadResponse = httpClient.GetAsync("http://www.google.fr/", HttpCompletionOption.ResponseHeadersRead).Result; 
       s.Stop(); 
       Console.WriteLine("First download Elapsed Time Download: {0} ms", s.ElapsedMilliseconds); 

       s = Stopwatch.StartNew(); 
       httpClient = new HttpClient(); 
       httpDownloadResponse = httpClient.GetAsync("http://www.google.fr/", HttpCompletionOption.ResponseHeadersRead).Result; 
       s.Stop(); 
       Console.WriteLine("Second download Elapsed Time Download: {0} ms", s.ElapsedMilliseconds); 
      }) 
     ); 
    } 
    Task.WaitAll(tasks.ToArray()); 
    while (Console.ReadLine() != null) ; 
} 
+0

@Adriano -'Console'은 threadsafe이고 타이밍에 포함되지 않습니다. –

+0

@BrianReischl 내 잘못! –

+1

관련이있을 수도 있지만 그렇지 않을 수도 있지만 .net에서 관찰되는 규칙은 특정 클라이언트 OS에서 동일한 URL에 대한 세 개의 HTTP 연결 만 허용된다는 것입니다. 이는 ServicePointManager.DefaultConnectionLimit에 정의되어 있습니다. 이것은 http://stackoverflow.com/questions/866350/how-can-i-programmatically-remove-the-2-connection-limit-in-webclient에서 다루어지며, 여러분은 병렬 http 활동에 대한 극적인 감속을 관찰하게 될 것입니다 같은 URL에 – PhillipH

답변

1

실제로 내 컴퓨터에 문제를 재현 할 수 없습니다 : 여기

는 문제가 재현하는 몇 가지 코드입니다. 내가하는 일과 상관없이 첫 번째 요청은 항상 거의 같은 시간이 걸리며 두 번째 요청은 더 빠릅니다.

가장 좋은 추측은 스레드 풀을 고갈시키고 있다는 것입니다. Task.Factory.StartNew에 전화 할 때마다 새로운 Task이 시작되며 새로운 스레드 풀 스레드가 필요합니다. 그런 다음 다른 스레드에서 다른 Task을 시작하고 첫 번째 스레드를 차단하는 HttpClient.GetAsync을 호출합니다. 따라서 각 요청에 대해 두 개의 스레드를 사용하게됩니다. 그렇게하면 스레드 풀의 모든 스레드가 사용되며 요청이 대기열에 들어가기 시작합니다. 스레드 풀은 더 많은 스레드를 추가하지만 천천히 - 일반적으로 새 스레드는 0.5 초마다 추가합니다. 그래서 그것은 그것의 일부가 될 수도 있습니다.

또한 HttpClient을 잘못 사용하고 있습니다. 각 HttpClient 인스턴스에는 연결 풀이 있으므로 하나의 인스턴스를 만들고 다시 사용하려는 것이 일반적입니다. 여기에 더 깨끗한 코드가 있습니다 - 시도해보고 문제가 해결되는지 확인하십시오.

public static void Main() 
{ 
    ServicePointManager.DefaultConnectionLimit = 200; 
    List<Task> tasks = new List<Task>(); 
    using (var client = new HttpClient()) 
    { 
     for (var i = 0; i < 10; i++) 
     { 
      tasks.Add(DoRequest(i, client)); 
     } 

     Task.WaitAll(tasks.ToArray()); 
    } 
} 

private async Task DoRequest(int id, HttpClient client) 
{ 
    const string url = "http://www.google.fr/"; 
    Stopwatch s = Stopwatch.StartNew(); 
    HttpResponseMessage httpDownloadResponse = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); 
    s.Stop(); 
    Console.WriteLine("Task {1} - First download Elapsed Time Download: {0} ms", s.ElapsedMilliseconds, id); 

    s = Stopwatch.StartNew(); 
    httpDownloadResponse = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); 
    s.Stop(); 
    Console.WriteLine("Task {1} - Second download Elapsed Time Download: {0} ms", s.ElapsedMilliseconds, id); 

} 
+0

당신의 솔루션은 작동하지만 이것을 할 수는 없습니다. 정말 다운로드를 시작하는 여러 스레드가 있어야합니다. 더 정확하게 말하면, 나는 메시지를받을 때 다운로드를 시작하는 MSMQ 대기열을 청취하면서 장시간 실행되는 10 개의 스레드를 갖게 될 것입니다. –

+0

ThreadPool.SetMinThreads (100, 100) 추가하기; 실행 시간을 훨씬 향상시킵니다. 올바른 동작을위한 최소 스레드는 number_tasks + 1 –

+0

입니다. 내 코드는 스레드를 사용하지만 더 적은 스레드를보다 효율적으로 사용합니다. 실행하면 병렬로 실행되는 것을 볼 수 있습니다. 프레임 워크에서는 프레임 워크가 각 요청을 시작하고 스레드 풀에서 I/O 완료 스레드 중 하나가 스레드를 완료하면 스레드 풀에있는 I/O 완료 스레드가 처리합니다. 이것은 요청마다 스레드를 묶는 것보다 훨씬 효율적입니다. 나는 실제로 테스트를 직접 실행했고, 비동기를이 방법을 사용하면 훨씬 더 확장 가능하고 더 빠르며 더 높은로드가된다. –