按钮单击时调用异步方法

Don*_*zov 49 c# wpf multithreading asynchronous windows-phone-8

我创建了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;
}
Run Code Online (Sandbox Code Playgroud)

它会挂在task.Wait()上.

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

谢谢您的帮助!

Sri*_*vel 85

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

不要阻止在UI线程中.永远不要这样做.等待它.

private async void Button_Click(object sender, RoutedEventArgs 
{
      var task = GetResponseAsync<MyObject>("my url");
      var items = await task;
}
Run Code Online (Sandbox Code Playgroud)

顺便问一下,你为什么要把WebException它扔掉呢?如果你根本不抓住它会更好.两者都是一样的.

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

同样使用"Async"后缀来返回一个Task或异步以遵循TAP("基于任务的异步模式")约定,如Jon所说.

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

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);
}
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 26

这就是杀了你的原因:

task.Wait();
Run Code Online (Sandbox Code Playgroud)

这是阻止UI线程直到任务完成 - 但任务是一个异步方法,它会在"暂停"并等待异步结果后尝试返回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
}
Run Code Online (Sandbox Code Playgroud)

要不就:

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

现在,在任务完成之前,该方法将在调度继续触发后返回,而不是阻塞Button_Click.(这就是async/await的工作方式,基本上.)

请注意,我也将重新命名GetResponse,以GetResponseAsync对清晰度.

  • @ChrisWalsh:是的,异步任务*将*仍然在您的主线程上运行。如果它确实是异步的(例如,执行异步 IO),那应该没问题。如果该任务执行阻塞操作,那就不好了。我建议您提出一个带有完整示例的新问题。 (2认同)