2012-11-29 2 views
1

Windows 8 "Metro"응용 프로그램에 단추와 텍스트 블록이 있습니다. 버튼을 클릭하면 HttpWebRequest를 통해 웹 서비스가 호출됩니다.Windows 8/WinRT의 다른 스레드 UI 요소 업데이트

private void buttonGo_Click(object sender, RoutedEventArgs e) 
{ 
    HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://localhost/"); 

    req.BeginGetResponse(ResponseCallback, req); 
} 

private void ResponseCallback(IAsyncResult asyncResult) 
{ 
    HttpWebRequest req = (HttpWebRequest)asyncResult.AsyncState; 

    HttpWebResponse res = (HttpWebResponse)req.EndGetResponse(asyncResult); 
    Stream streamResponse = res.GetResponseStream(); 
    StreamReader streamRead = new StreamReader(streamResponse); 
    string responseString = streamRead.ReadToEnd(); 

    info.Text = responseString; // Can't do this as outside the UI thread 
} 

내가 WebRequest 클래스에서 반환 된 데이터로 info.Text를 업데이트 할, 그러나 그것은 오류가 발생합니다 : 는 "응용 프로그램이 다른 스레드 정렬 화 된 인터페이스를했다." 이것이 UI 스레드에서 호출되지 않기 때문입니다.

나는 Dispatcher, SynchronizationContext, await 키워드와 관련된 다양한 솔루션을 발견했습니다.

가장 쉬운 방법은 무엇입니까?

+1

,하지만 난 (()가 info.Dispatcher.Invoke'처럼 뭔가 될 것 = 상상이가 UI 스레드로 호출을 전달 현재 CoreDispatcher를 사용하여 수행됩니다 .Text = responseString); ' –

답변

2

Windows Store 앱에서 모든 비동기 호출에 대해 비동기 대기 패턴을 사용해야합니다. 이는 가장 간단하고 효과적인 방법입니다.

private async void buttonGo_Click(object sender, RoutedEventArgs e) 
{ 
    HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://localhost/"); 

    HttpWebResponse res = (HttpWebResponse)(await req.GetResponseAsync()); 
    Stream streamResponse = res.GetResponseStream(); 
    StreamReader streamRead = new StreamReader(streamResponse); 
    // all IO operations should be called asynchronously 
    string responseString = await streamRead.ReadToEndAsync(); 

    info.Text = responseString; // This way the code runs on UI thread 
} 

각 이후의 코드는 콜백에 있었지만 그렇게하지 않도록 항상 UI 스레드에서 실행하는 것처럼 키워드가 효과적으로 작동하고 기다리고 다음과 같이이 패턴을 사용하는 기존 코드를 다시 작성할 수 있습니다 그것에 대해 걱정해야합니다.

Windows 용 API의 모든 IO 바운드 작업 Store 응용 프로그램은 비동기 형식으로 사용할 수 있습니다. 위의 ReadToEndAsync 예와 같이 UI 스레드 차단을 방지하기 위해 둘 다 사용할 수있는 경우에도 동기식 UI보다 더 선호합니다.

1

는 가장 쉬운 최상의 : 내부적으로

private async void buttonGo_Click(object sender, RoutedEventArgs e) 
{ 
    using (var client = new HttpClient()) 
    { 
    info.Text = await client.GetStringAsync("http://localhost/"); 
    } 
} 

, await 현재 SynchronizationContext을 캡처하고 그 맥락에서 방법을 다시 시작합니다. Win8/WinRT SynchronizationContext은 차례로 Dispatcher을 사용합니다.

+0

ResponseCallback 내에서 info.Text를 구체적으로 설정하려면 어떻게해야합니까? – zi3guw

+0

글쎄,'TaskCompletionSource'를 사용하여 전체 콜백을'Task '에 래핑 한 다음 내가 게시 한 코드를 사용할 수 있습니다. 아니면'SynchronizationContext'를 포착하고 스레드를 수동으로 마샬링 할 수 있습니다. 왜 요즘에는 그렇게 할 수 있을지 확신 할 수 없습니다. –

+0

한 가지 이유는 .net 4.0에서 비동기 진행을 원할 때입니다. .net 4.5 및 후자를 사용하는 경우 IProgress <> 또는 진행 <>이 있으므로 제대로 이해하면 TaskCompletionSource 만 사용할 수 있습니다. .net 4.0에서 운이 좋지 않으므로 진행자를 호출자에게 전달하기 위해 SynchronizationContext를 사용해야 할 필요가 있습니까? – Wes

4

마찬가지로 Damir는 비동기/대기 패턴을 사용해야한다고 말했지만 작업자 스레드 (작업)의 UI를 업데이트하기 만하면됩니다. > 정보를 내가 특별히 WinRT를 사용하지 않은

private void ResponseCallback(IAsyncResult asyncResult) 
{ 
    ... 

    this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,() => 
    { 
     info.Text = responseString; 
    }); 
}