2012-11-23 3 views
16

저는 C# 5의 비동기 기능을 처음 사용합니다. 나는이 두 가지 구현 사이의 차이를 이해하려고 노력 해요 :Async-await Task.Run vs HttpClient.GetAsync

구현을 1 :

private void Start() 
{ 
    foreach(var url in urls) 
    { 
     ParseHtml(url); 
    } 
} 

private async void ParseHtml(string url) 
{ 
    var query = BuildQuery(url); //BuildQuery is some helper method 
    var html = await DownloadHtml(query); 
    //... 
    MyType parsedItem = ParseHtml(html); 
    SaveTypeToDB(parsedItem); 
} 

private async Task<string> DownloadHtml(string query) 
{ 
    using (var client = new HttpClient()) 
    try 
    { 
     var response = await client.GetAsync(query); 
     return (await response.Content.ReadAsAsync<string>()); 
    } 
    catch (Exception ex) 
    { 
     Logger.Error(msg, ex); 
     return null; 
    } 
} 

구현 2 :

private void DoLoop() 
{ 
    foreach(var url in urls) 
    { 
     Start(url); 
    } 
} 

private async void Start(url) 
{ 
    await Task.Run(() => ParseHtml(url)) ; 
} 

private void ParseHtml(string url) 
{ 
    var query = BuildQuery(url); //BuildQuery is some helper method 
    var html = DownloadHtml(query); 
    //... 
    MyType parsedItem = ParseHtml(html); 
    SaveTypeToDB(parsedItem); 
} 

private string DownloadHtml(string query) 
{ 
    using (var client = new WebClient()) 
    { 
     try 
     { 
      return client.DownloadString(query); 
     } 
     catch (Exception ex) 
     { 
      Logger.Error(msg, ex); 
      return null; 
     } 
    } 
} 

는 차라리 두 번째 구현을 사용하십시오 그것은 내 코드에서 메소드에 대한 비동기 서명이 덜 필요하기 때문입니다. 나는 HttpClient 클래스를 사용하는 것이 새로운 작업을 사용하고 대신 그것을 기다리는 이점을 이해하려고 노력하고있다.

두 구현간에 차이점이 있습니까?

+0

' 'async'signatures' - 그들에 대해 신경 쓰지 마라. "비동기"는 이전 코드와의 호환성을 유지하는 "마커"일뿐입니다. 5.0 이전 버전에서는 "await"라는 이름의 변수/함수를 사용할 수있었습니다. 그러나 오래된 메서드는 "비동기"키워드가 없기 때문에 코드는 C# 5.0을 사용하여 여전히 컴파일됩니다 (변경없이). – igrimpe

답변

28

필자는 코드에서 메서드에 '비동기'서명이 덜 필요하므로 두 번째 구현을 사용하고 싶습니다.

그게 매우 이상한 것 같습니다. 근본적으로 "다소 비동기 적으로"실행하려고 노력하고 있습니다. 그렇다면 왜 그렇게 명확하게하지 않습니까?

두 구현간에 차이점이 있습니까?

물론입니다. 두 번째 구현에서는 스레드를 연결하고 WebClient.DownloadString 블록이 완료되면 요청이 완료 될 때까지 대기합니다. 첫 번째 버전에는 차단 된 스레드가 없습니다. 요청이 완료되면 계속해서 실행됩니다.

또한 Logger.Error 전화를 고려하십시오. 비동기 버전에서는 여전히 원래 호출 코드의 컨텍스트에서 실행됩니다. 따라서 이것이 Windows Forms UI에 있다면 UI 스레드에 계속있을 것이며 UI 요소 등에 액세스 할 수 있습니다. 두 번째 버전에서는 스레드 풀 스레드에서 실행하게 될 것이므로 UI 스레드를 다시 마샬하여 UI를 업데이트합니다.

async void 방법이 거의 확실하게 일 때async void이 아니어야합니다. async 메서드는 이벤트 처리기 시그니처를 준수하기 위해 void 만 반환해야합니다. 다른 모든 경우에는 Task을 반환 - 당신의 작업이 완료되면 호출자가 볼 수있는 방법 등 예외를 처리

또한 비동기에 대한 HttpClient를 사용할 필요가 없습니다 있습니다 - 당신이 그렇게하는 대신 WebClient.DownloadStringTaskAsync를 사용할 수 귀하 마지막 방법은 될 수 : 스레드 풀의 효율성을 높이고 아마도 당신의 프로그램이 더 많은 사용자로 확장 할 수 있도록 : 서버 응용 프로그램에 대한

private async Task<string> DownloadHtml(string query) 
{ 
    using (var client = new WebClient()) 
    { 
     try 
     { 
      return await client.DownloadStringTaskAsync(query); 
     } 
     catch (Exception ex) 
     { 
      Logger.Error(msg, ex); 
      return null; 
     } 
    } 
} 
+0

고마워요! 빠른 질문. 첫 번째 구현에서 ParseHtml을 살펴 보겠습니다. DownloadHtml에서 전달 된 Task 객체를 어떻게 반환합니까? 그 값을 사용할 때 "MyType"으로 변환됩니다. – vondip

+1

@vondip : 무슨 뜻인지 이해가 안됩니다. 아마도 그 질문을 별도의 질문으로 더 자세히 말할 수 있습니까? –

6

async 당신이있어 차단 된 스레드의 수를 최소화에 관한 것입니다.

스레드 수를 신경 쓸 필요가없는 클라이언트 응용 프로그램의 경우 async은 I/O 수행시 UI를 유동적으로 유지하는 비교적 쉬운 방법을 제공합니다.

두건 아래의 Task.Run과 많이 다릅니다.

1

UI를 응답 성있게 유지하는 것과 같이 호출 스레드가 의미있는 작업을 수행하는 경우에만 비동기 처리의 이점을 얻을 수 있습니다. 호출 스레드가 작업 만 시작하고 작업이 끝날 때까지 대기하지만 아무 것도하지 않으면 프로세스가 느리게 실행됩니다.

두 번째 구현은 작업을 시작하지만 다른 작업을 수행하지 않고 끝내기를 기다리고 있습니다. 이렇게하면 도움이되지 않습니다.

당신은 당신의 환경을 설명하지 않지만, 당신은 UI가있는 경우 반응 유지해야 그 다음 방법 (1)의 구현은 시작() 비동기 선언되지 않고 기다리고하지 않는 것을 제외하고 괜찮 :

다음과 같이
private async Task StartAsync() 
{ 
    foreach (var url in urls) 
    { 
     await ParseHtml(url) 
    } 
} 

당신은 이벤트 처리기에서이 호출 할 수 있습니다 :

private async void OnButton1_clicked(object sender, ...) 
{ 
    await StartAsync(); 
} 

참고 : ParseHtml 기다리고 앞에는있다. 다음 html은 이전 구문 분석이 완료된 후에 구문 분석됩니다. 그러나 구문 분석이 비동기이기 때문에 호출 스레드 (UI 스레드?)는 사용자 입력에 응답하는 것과 같은 다른 작업을 수행 할 수 있습니다. 당신의 parseHTML 기능을 동시에 실행할 수있는 경우

그러나, 다음 코드는 아마 빠른 바람직하고, 것입니다 : 당신이 작업을 반환하는 함수를 호출 할 경우

private async Task StartAsync() 
{ 
    var tasks = new List<Task>() 
    foreach (var url in urls) 
    { 
     tasks.Add(ParseHtml(url)); 
    } 
    // now you have a sequence of Tasks scheduled to be run, possibly simultaneously. 
    // you can do some other processing here 
    // once you need to be certain that all tasks are finished await Task.WhenAll(...) 
    await Task.WhenAll(tasks); 
    // Task.WhenAls(...) returns a Task, hence you can await for it 
    // the return of the await is a void 
} 
  • , 당신은 다른 일을 계속할 수 있습니다 작업이 실행되는 동안 또는 작업이 완료되고 작업 결과를 사용할 때까지 대기합니다.
  • 코드가 멈추지 만 호출자는 작업 완료를 기다릴 때까지 계속 처리합니다.
  • 절차가 비동기 인 경우에만 기다릴 수 있습니다.
  • 비동기 함수는 다른 비동기 함수에 의해서만 호출되거나 Task.Run (() => ...) 호출하거나 선호하는 경우 Task.Factory.StartNew (() => ...)
  • 대신 빈의 비동기 기능이
  • 작업
  • 대신 TResult의 유일한 예외는 이벤트 핸들러 인 비동기 함수 반환 작업 <TResult>
  • 를 반환은 비동기 선언하고 무효 돌아갑니다.
  • 작업을 완료해야하는 경우 작업을 기다리고 있습니다.
  • 기다림의 반환은 TResult입니다.