按钮单击时调用异步方法

时间:2015-02-19 08:33:43

标签: c# wpf multithreading windows-phone-8 asynchronous

我创建了Windows Phone 8.1项目,我试图在按钮单击时运行异步方法GetResponse(字符串url)并等待方法完成,但方法永远不会完成。这是我的代码:

private void Button_Click(object sender, RoutedEventArgs 
{
      Task<List<MyObject>> task = GetResponse<MyObject>("my url");
      task.Wait();
      var items = task.Result; //break point here
}

public static async Task<List<T>> GetResponse<T>(string url)
{
    List<T> items = null;
    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);

    var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
    try
    {
        Stream stream = response.GetResponseStream();
        StreamReader strReader = new StreamReader(stream);
        string text = strReader.ReadToEnd();
        items = JsonConvert.DeserializeObject<List<T>>(text);
    }
    catch (WebException)
    {
        throw;
    }
    return items;
}

它会挂在task.Wait()。

我将按钮单击方法更改为异步并在异步方法之前使用等待,我得到结果(await GetResponse<string>("url"))。 Task<List<string>> task = GetResponse<string>("url")有什么问题?  我做错了什么?

感谢您的帮助!

3 个答案:

答案 0 :(得分:71)

你是经典僵局的受害者。 task.Wait()task.Result是UI线程中的阻塞调用,导致死锁。

Don't block in the UI thread。永远不要这样做。等待它。

private async void Button_Click(object sender, RoutedEventArgs 
{
      var task = GetResponseAsync<MyObject>("my url");
      var items = await task;
}
顺便问一下,你为什么要抓住WebException然后扔掉它?如果你根本不抓住它会更好。两者都是一样的。

此外,我可以看到您在GetResponse方法中将异步代码与同步代码混合在一起。 StreamReader.ReadToEnd是阻止通话 - 您应该使用StreamReader.ReadToEndAsync

同样使用&#34; Async&#34;返回任务或异步的方法的后缀,以遵循TAP(&#34;基于任务的异步模式&#34;)约定为Jon says

当您解决上述所有问题时,您的方法应如下所示。

public static async Task<List<T>> GetResponseAsync<T>(string url)
{
    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
    var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);

    Stream stream = response.GetResponseStream();
    StreamReader strReader = new StreamReader(stream);
    string text = await strReader.ReadToEndAsync();

    return JsonConvert.DeserializeObject<List<T>>(text);
}

答案 1 :(得分:24)

这就是杀了你的原因:

task.Wait();

在任务完成之前阻止UI线程 - 但该任务是一个异步方法,它会在它停止&#34;暂停&#34;之后尝试返回到UI线程。并等待异步结果。它无法做到这一点,因为您正在阻止UI线程......

你的代码中没有任何东西看起来真的需要在UI线程上,但假设你真的想要它,你应该使用:

private async void Button_Click(object sender, RoutedEventArgs 
{
    Task<List<MyObject>> task = GetResponse<MyObject>("my url");
    var items = await task;
    // Presumably use items here
}

或者只是:

private async void Button_Click(object sender, RoutedEventArgs 
{
    var items = await GetResponse<MyObject>("my url");
    // Presumably use items here
}

现在代替阻止直到任务完成,Button_Click方法将在任务完成后安排连续触发后返回。 (那就是async / await如何工作,基本上。)

请注意,为清晰起见,我还会将GetResponse重命名为GetResponseAsync

答案 2 :(得分:0)

使用下面的代码

 Task.WaitAll(Task.Run(async () => await GetResponse<MyObject>("my url")));