2011-09-20 4 views
2

내 응용 프로그램에 액세스하려는 JSON API가 있습니다. 그래서 나는 방법을 썼다.비동기 메서드 주위에 래퍼를 작성하는 방법은 무엇입니까?

public List<Books> GetBooks() 
{ 
    var webclient = new WebClient(); 
    var jsonOutput = webclient.DownloadString(
         new Uri("http://someplace.com/books.json") 
          ); 

    return ParseJSON(jsonOutput);//Some synchronous parsing method 
} 

이제 DonwloadString을 DownloadStringAsync로 변경해야합니다. 이 부분을 찾았습니다. tutorial.

하지만 이것은 너무 복잡해 보입니다. 이 작업을 수행하려고하는데, 이것이 올바른 방법인지 확실하지 않습니다. 아마도 더 간단하고 나은 방법이 있을까요?

+0

무엇을 주위에 감싸고 싶습니까? –

+0

결국에는 GetBooksAsync()라는 비동기 메서드를 갖고 싶습니다. GetBooksCompleted도 가질 수 있습니다. –

답변

7

결과는 단지 고통스러운 얻을 수있는 이벤트를 구독해야하는 비동기 작업의 전체를 사용하여 단지 시도 할 수 있습니다. 가장 간단한 방법은 이벤트 처리를 멋진 확장 메서드로 추상화하고 결과를 처리하기 위해 연속 통과 스타일 (CPS)을 사용하는 것입니다.

그래서, 우선 다운로드 문자열 확장 메서드를 작성하는 것입니다 :

public static void DownloadString(this Uri uri, Action<string> action) 
{ 
    if (uri == null) throw new ArgumentNullException("uri"); 
    if (action == null) throw new ArgumentNullException("action"); 

    var webclient = new WebClient(); 

    DownloadStringCompletedEventHandler handler = null; 
    handler = (s, e) => 
    { 
     var result = e.Result; 
     webclient.DownloadStringCompleted -= handler; 
     webclient.Dispose(); 
     action(result); 
    }; 

    webclient.DownloadStringCompleted += handler; 
    webclient.DownloadStringAsync(uri); 
} 

이 방법은 WebClient의 생성을 멀리 숨기고, 이벤트의 모든 처리 및 처분과 사물을 청소 탈퇴 나중에.

는 그것은 다음과 같이 사용되는 :

var uri = new Uri("http://someplace.com/books.json"); 
uri.DownloadString(t => 
{ 
    // Do something with the string 
}); 

지금 이것은 GetBooks 방법을 만들 수 있습니다. 여기있다 :

public void GetBooks(Uri uri, Action<List<Books>> action) 
{ 
    if (action == null) throw new ArgumentNullException("action"); 
    uri.DownloadString(t => 
    { 
     var books = ParseJSON(t); 
     action(books); 
    }); 
} 

는 그것은 다음과 같이 사용되는 : 단정하고 간단해야

this.GetBooks(new Uri("http://someplace.com/books.json"), books => 
{ 
    // Do something with `List<Books> books` 
}); 

.

이제 두 가지 방법으로 확장 할 수 있습니다.

당신이 서명이 ParseJSON의 과부하 만들 수

:

void ParseJSON(string text, Action<List<Books>> action) 

그런 다음 당신이 전부 GetBooks 방법과 멀리 할 수있는 그냥이 쓰기 : 이제

var uri = new Uri("http://someplace.com/books.json"); 
uri.DownloadString(t => ParseJSON(t, books => 
{ 
    // Do something with `List<Books> books` 
    // `string t` is also in scope here 
})); 

당신이 좋은이를 깔끔한 유창한 스타일의 구성 가능한 작업 세트. 보너스로 다운로드 한 문자열 t도 범위에 있으므로 필요한 경우 쉽게 기록하거나 다른 처리를 할 수 있습니다.

또한 예외를 처리 할 수 ​​있으며, 이들과 같이 추가 할 수 있습니다 : 다음

public static void DownloadString(this Uri uri, Action<string> action) 
{ 
    uri.DownloadString(action, null); 
} 

그리고에 : 당신은 다음과 DownloadString 확장 메서드를 처리되지 않은 오류를 대체 할 수

public static void DownloadString(
    this Uri uri, 
    Action<string> action, 
    Action<Exception> exception) 
{ 
    if (uri == null) throw new ArgumentNullException("uri"); 
    if (action == null) throw new ArgumentNullException("action"); 

    var webclient = (WebClient)null; 

    Action<Action> catcher = body => 
    { 
     try 
     { 
      body(); 
     } 
     catch (Exception ex) 
     { 
      ex.Data["uri"] = uri; 
      if (exception != null) 
      { 
       exception(ex); 
      } 
     } 
     finally 
     { 
      if (webclient != null) 
      { 
       webclient.Dispose(); 
      } 
     } 
    }; 

    var handler = (DownloadStringCompletedEventHandler)null;   
    handler = (s, e) => 
    { 
     var result = (string)null; 
     catcher(() => 
     { 
      result = e.Result; 
      webclient.DownloadStringCompleted -= handler; 
     }); 
     action(result); 
    }; 

    catcher(() => 
    { 
     webclient = new WebClient(); 
     webclient.DownloadStringCompleted += handler; 
     webclient.DownloadStringAsync(uri); 
    }); 
} 

다음과 같이 오류 처리 방법을 사용하십시오.

var uri = new Uri("http://someplace.com/books.json"); 
uri.DownloadString(t => ParseJSON(t, books => 
{ 
    // Do something with `List<Books> books` 
}), ex => 
{ 
    // Do something with `Exception ex` 
}); 

최종 결과는 사용 및 읽기가 상당히 쉬워야합니다. 이게 도움이 되길 바란다.

+0

"결과를 얻기 위해 이벤트에 가입해야하는 비동기 작업이 고통 스럽다"는 것에는 동의하지 않지만 매우 잘 만들어진 솔루션입니다. 잘 했어. – Mac

+0

잘 했어! 결국 나는 더 간단한 접근 방식으로 해결했다. –

0

ASP.NET 응용 프로그램을 작성하지 않는다고 가정합니다.

백그라운드 워커 구성 요소를 사용해 보았습니까? UI를 묶어서는 안되는 장기 실행 작업의 경우 멀티 스레딩 기능을 쉽고 간편하게 사용할 수 있습니다. 예를 들어 ProgressChanged 이벤트를 사용하여 UI 업데이트를 수행 할 수 있으며 백그라운드 작업자와 백그라운드 작업자 클래스는 BW를 만든 스레드가 ProcessChanged 및 WorkComplete 이벤트를 실행하는 스레드인지 확인합니다. 따라서 UI에서 BW를 ​​만들고 작동하도록 설정 한 경우 UI를 안전하게 업데이트 할 수 있습니다. 여기

빠른 MS http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx에서 기사

또 다른 정말 좋은 링크 http://www.albahari.com/threading/part3.aspx#_BackgroundWorker

--edit-- 내가 링크를 쳐다 보면서 그가이 일을 할 나타나는 것은 Cooporative 취소의 전체 구현입니다 무늬. 여기서 백그라운드 스레드는 일상적으로 변수를 검사하고 사실인지 여부를 확인하여 취소를 정상적으로 지원합니다. BW는이 패턴의 구현입니다.

당신이 정말로 간단한 무언가를 원하는 경우에

다음 당신은 ThreadPool이

ThreadPool.QueueUserWorkItem(DoWork); 
public void DoWork(){ 
    //Just remember that this code happens in a seperate thread so don't update 
    //the UI. It will throw an exception. You would need to call 
    //Form.BeginInvoke(UpdateFunction) in order to update the UI 
    DoSomethingInteresting(); 
} 
관련 문제