我创建了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")
有什么问题?
我做错了什么?
感谢您的帮助!
答案 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")));