it-swarm.com.de

Aufrufen der asynchronen Methode beim Klicken auf die Schaltfläche

Ich habe ein Windows Phone 8.1-Projekt erstellt und versuche, die asynchrone Methode GetResponse (Zeichenfolge-URL) beim Klicken auf die Schaltfläche auszuführen und auf den Abschluss der Methode zu warten, aber die Methode wird nie abgeschlossen. Hier ist mein Code:

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;
}

Es wird an der Aufgabe hängen. Warten ().

Ich habe meine Button-Click-Methode in async geändert und wait vor der async-Methode verwendet, und ich erhalte das Ergebnis (await GetResponse<string>("url")). Was ist falsch an Task<List<string>> task = GetResponse<string>("url")? Was mache ich falsch?

Danke für die Hilfe!

36
Doniyor Niyozov

Du bist das Opfer des klassischen Deadlocks. task.Wait() oder task.Result ist ein blockierender Aufruf im UI-Thread, der den Deadlock verursacht.

Nicht im UI-Thread blockieren . Mache das niemals. Warten Sie einfach darauf.

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

Übrigens, warum fängst du die WebException und wirfst sie zurück? Es wäre besser, wenn Sie es einfach nicht fangen. Beide sind gleich.

Ich kann auch sehen, dass Sie den asynchronen Code mit synchronem Code in der GetResponse-Methode mischen. StreamReader.ReadToEnd ist ein blockierender Aufruf - Sie sollten StreamReader.ReadToEndAsync verwenden.

Verwenden Sie das Suffix "Async" auch für Methoden, die eine Task oder asynchron zurückgeben, um der TAP-Konvention ("Task based Asynchronous Pattern") zu folgen, wie Jon sagt .

Ihre Methode sollte in etwa wie folgt aussehen, wenn Sie alle oben genannten Bedenken angesprochen haben.

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);
}
64

Das bringt dich um:

task.Wait();

Das blockiert den UI-Thread, bis die Aufgabe abgeschlossen ist - aber die Aufgabe ist eine asynchrone Methode, die versucht, zum UI-Thread zurückzukehren, nachdem sie "angehalten" hat und auf ein asynchrones Ergebnis wartet. Das geht nicht, weil Sie den UI-Thread blockieren ...

Es gibt nichts in Ihrem Code, das wirklich so aussieht, als müsste es sich auf dem UI-Thread befinden, aber wenn Sie es wirklich wollen , sollten Sie Folgendes verwenden:

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

Oder nur:

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

Anstatt jetzt zu blockieren , bis die Aufgabe abgeschlossen ist, wird Button_Click -Methode wird nach dem Planen einer Fortsetzung zum Feuern zurückgegeben, wenn die Aufgabe abgeschlossen ist. (So ​​funktioniert async/await im Grunde.)

Beachten Sie, dass ich aus Gründen der Übersichtlichkeit auch GetResponse in GetResponseAsync umbenennen würde.

25
Jon Skeet

verwenden Sie den folgenden Code

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