1

我在 Windows 8“Metro”应用程序中有一个按钮和一个文本块。单击按钮时,它会通过 HttpWebRequest 调用 Web 服务。

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
}

我想info.Text用从 WebRequest 返回的数据进行更新,但是它会导致错误:“应用程序调用了一个为不同线程编组的接口。” 我知道这是因为它不是从 UI 线程调用的。

我发现了各种不同的解决方案,涉及Dispatcher, SynchronizationContext,await关键字。

最简单/最好的方法是什么?

4

3 回答 3

4

正如 Damir 所说,我们确实应该使用 async/await 模式,但有时只需在工作线程(任务)中更新 UI。这是使用将调用分派到 UI 线程的当前 CoreDispatcher 完成的:

private void ResponseCallback(IAsyncResult asyncResult)
{
   ...

   this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
   {
      info.Text = responseString;
   });
}
于 2012-11-29T07:02:47.730 回答
2

在 Windows 应用商店应用程序中,您确实应该对所有异步调用使用 async await 模式——这是最简单和最有效的方法。您可以重写现有代码以使用此模式,如下所示:

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
}

每个 await 关键字之后的代码实际上就像在回调中一样,但它始终在 UI 线程上执行,因此您不必担心它。

适用于 Windows 应用商店应用程序的 API 中的所有 IO 绑定操作均以异步方式提供。你应该更喜欢它们而不是同步的,即使两者都可以防止 UI 线程阻塞——ReadToEndAsync例如上面的例子。

于 2012-11-29T05:40:56.707 回答
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/WinRTSynchronizationContext反过来使用Dispatcher.

于 2012-11-29T01:35:09.807 回答