2012-01-15 4 views
6

awaitasync이 여기 내 코드의 성능을 향상시키지 않는 이유는 무엇입니까? like they're supposed to.비동기 CTP가 제대로 작동하지 않는 이유는 무엇입니까?

비록 회의적 이었지만 컴파일러가 내 방법을 다시 작성하여 다운로드가 병렬로 이루어지기를 원했지만 실제로는 그렇지 않은 것 같습니다.
(나는이 awaitasync 별도의 스레드를 생성하지 않는 것을 깨닫게 할 ;? 그러나, OS는 parallal에서 다운로드하고, 원래의 스레드에서 내 코드를 다시 호출해야한다 -해야하지 그것을)

asyncawait을 부적절하게 사용하고 있습니까? 그들을 사용하는 적절한 방법은 무엇입니까?

코드 :

using System; 
using System.Net; 
using System.Threading; 
using System.Threading.Tasks; 

static class Program 
{ 
    static int SumPageSizesSync(string[] uris) 
    { 
     int total = 0; 
     var wc = new WebClient(); 
     foreach (var uri in uris) 
     { 
      total += wc.DownloadData(uri).Length; 
      Console.WriteLine("Received synchronized data..."); 
     } 
     return total; 
    } 

    static async Task<int> SumPageSizesAsync(string[] uris) 
    { 
     int total = 0; 
     var wc = new WebClient(); 
     foreach (var uri in uris) 
     { 
      var data = await wc.DownloadDataTaskAsync(uri); 
      Console.WriteLine("Received async'd CTP data..."); 
      total += data.Length; 
     } 
     return total; 
    } 

    static int SumPageSizesManual(string[] uris) 
    { 
     int total = 0; 
     int remaining = 0; 
     foreach (var uri in uris) 
     { 
      Interlocked.Increment(ref remaining); 
      var wc = new WebClient(); 
      wc.DownloadDataCompleted += (s, e) => 
      { 
       Console.WriteLine("Received manually async data..."); 
       Interlocked.Add(ref total, e.Result.Length); 
       Interlocked.Decrement(ref remaining); 
      }; 
      wc.DownloadDataAsync(new Uri(uri)); 
     } 
     while (remaining > 0) { Thread.Sleep(25); } 
     return total; 
    } 

    static void Main(string[] args) 
    { 
     var uris = new string[] 
     { 
      // Just found a slow site, to demonstrate the problem :) 
      "http://www.europeanchamber.com.cn/view/home", 
      "http://www.europeanchamber.com.cn/view/home", 
      "http://www.europeanchamber.com.cn/view/home", 
      "http://www.europeanchamber.com.cn/view/home", 
      "http://www.europeanchamber.com.cn/view/home", 
     }; 
     { 
      var start = Environment.TickCount; 
      SumPageSizesSync(uris); 
      Console.WriteLine("Synchronous: {0} milliseconds", Environment.TickCount - start); 
     } 
     { 
      var start = Environment.TickCount; 
      SumPageSizesManual(uris); 
      Console.WriteLine("Manual: {0} milliseconds", Environment.TickCount - start); 
     } 
     { 
      var start = Environment.TickCount; 
      SumPageSizesAsync(uris).Wait(); 
      Console.WriteLine("Async CTP: {0} milliseconds", Environment.TickCount - start); 
     } 
    } 
} 

출력 :

Received synchronized data... 
Received synchronized data... 
Received synchronized data... 
Received synchronized data... 
Received synchronized data... 
Synchronous: 14336 milliseconds 
Received manually async data... 
Received manually async data... 
Received manually async data... 
Received manually async data... 
Received manually async data... 
Manual: 8627 milliseconds   // Almost twice as fast... 
Received async'd CTP data... 
Received async'd CTP data... 
Received async'd CTP data... 
Received async'd CTP data... 
Received async'd CTP data... 
Async CTP: 13073 milliseconds  // Why so slow?? 

답변

10

Chris의 대답은 거의 정확하지만 경쟁 조건을 도입하고 모든 작업을 동 기적으로 차단합니다.

일반적으로 await/async을 사용할 수있는 경우 작업 계속을 사용하지 않는 것이 가장 좋습니다. 또한 WaitAny/WaitAll을 사용하지 마십시오. asyncWhenAnyWhenAll입니다.

나는이처럼 작성합니다 ...

static async Task<int> SumPageSizesAsync(IEnumerable<string> uris) 
{ 
    // Start one Task<byte[]> for each download. 
    var tasks = uris.Select(uri => new WebClient().DownloadDataTaskAsync(uri)); 

    // Asynchronously wait for them all to complete. 
    var results = await TaskEx.WhenAll(tasks); 

    // Calculate the sum. 
    return results.Sum(result => result.Length); 
} 
+0

아, 이건 훨씬 깨끗해 보입니다. =) 고마워! – Mehrdad

1

내가 코드를 오독 될 수 있지만, 당신이 기다리고, 비동기 읽기를 수행하는 백그라운드 스레드를 실행 후 즉시 차단하고있는 것 같습니다 그것을 완료하십시오. 코드의 '비동기'부분에 대해서는 실제로 비동기입니다.

static async Task<int> SumPageSizesAsync(string[] uris) 
{ 
    int total = 0; 
    var wc = new WebClient(); 
    var tasks = new List<Task<byte[]>>(); 
    foreach (var uri in uris) 
    { 
     tasks 
      .Add(wc.DownloadDataTaskAsync(uri).ContinueWith(() => { total += data.Length; 
     })); 
    } 
    Task.WaitAll(tasks); 
    return total; 
} 

를 그리고 이렇게 사용 :이 시도

{ 
     var start = Environment.TickCount; 
     await SumPageSizesAsync(uris); 
     Console.WriteLine("Async CTP: {0} milliseconds", Environment.TickCount - start); 
    } 

내가 비동기 물건이 새로운 뭐가 잘못 될 수 난 - 그것을하지만 동기에 비슷한시기에 100 %에 익숙하지 않다 버전이 나를 참아내는 것 같습니다.

+0

흠을 그래서 나는 다음 IS 논리적 인 질문을 추측을 할 수있는 * 정확한 * 방법은 무엇입니까? – Mehrdad

+0

어떻게 생각하는지 생각을 더하기 위해 편집되었습니다. 다시 말하지만 완전히 틀릴 수 있습니다. –

+0

감사! ''기다리는 것 '의 핵심은 나머지 메서드를 연속 전달 스타일로 변경하는 것입니다. (그리고 비동기 CTP의 전체 요점은 대리자/람다와 함께 많은 배관을 할 필요가 없기 때문입니다. 이것은 예와 다르다. 'BeginInvoke'와 그 밖의 것들은 .NET 2.0 이후로 이미 가지고 있습니다. – Mehrdad

관련 문제