2016-06-05 2 views
2

Я просто следую примеру с вызовом HttpClient в режиме синхронизации, он отлично работает в консольном приложении.Использование await on HttpClient в проекте wpf

Однако, когда я перехожу к приложению wpf, программа висит без возврата.

Я пытаюсь изолировать проблему, создав отдельный класс для обработки фиктивного запроса, чтобы посетить www.google.com.

Похоже, что приложение зависает при вызове client.GetAsync, могу ли я узнать, нужно ли что-то менять с консольного приложения на wpf в этом случае?

Пожалуйста, найти источник как консольное приложение и МОФ, как показано ниже,

Console приложение - работает отлично:

using System; 
using System.Threading.Tasks; 
using System.Net.Http; 

namespace ca03 
{ 
    static class action 
    { 
     static async Task<string> DownloadPageAsync() 
     { 
      // ... Target page. 
      string page = "http://www.google.com/"; 

      // ... Use HttpClient. 
      using (HttpClient client = new HttpClient()) 
      using (HttpResponseMessage response = await client.GetAsync(page)) 
      using (HttpContent content = response.Content) 
      { 
       // ... Read the string. 
       string result = await content.ReadAsStringAsync(); 

       // ... Display the result. 
       if (result != null && 
       result.Length >= 50) 
       { 
        Console.WriteLine(result.Substring(0, 50) + "..."); 
       } 
       return result; 
      } 
     } 

     public static string goDownload() 
     { 
      Task<string> x = DownloadPageAsync(); 
      string result = x.Result; 
      return result; 
     } 

    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      string data = action.goDownload(); 
      Console.WriteLine(data); 

      Console.ReadLine(); 
     } 
    } 
} 

WPF приложения (только простой проект с помощью кнопки на добавленную стоимость) - висеть в GetAsync

using System; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Net.Http; 

namespace wpf02 
{ 

    static class action 
    { 
     static async Task<string> DownloadPageAsync() 
     { 
      // ... Target page. 
      string page = "http://www.google.com/"; 

      // ... Use HttpClient. 
      using (HttpClient client = new HttpClient()) 
      using (HttpResponseMessage response = await client.GetAsync(page)) 
      using (HttpContent content = response.Content) 
      { 
       // ... Read the string. 
       string result = await content.ReadAsStringAsync(); 
       return result; 
      } 
     } 

     public static string goDownload() 
     { 
      Task<string> x = DownloadPageAsync(); 
      string result = x.Result; 
      return result; 
     } 

    } 


    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private void button_Click(object sender, RoutedEventArgs e) 
     { 
      string data = action.goDownload(); 
      Console.WriteLine(data); 
     } 
    } 
} 
+0

Я иду в эту проблему более подробно [на моем блоге] (http://blog.stephencleary.com/2012/07 /dont-block-on-async-code.html). –

ответ

5

Эта линия проблема:

string result = x.Result; 

Когда вы используете свойство Result, которое блокирует текущий поток, пока задача не завершится. Это проблема, когда ваши продолжения предназначены для работы на одной и ту же тему ...

В принципе, нет никакого смысла в методе goDownload - просто изменить метод DownloadPageAsync быть публичными, а затем изменить метод button_Click к также быть асинхронной, так что вы можете ждать результата:

private async void button_Click(object sender, RoutedEventArgs e) 
{ 
    string data = await action.DownloadPageAsync(); 
    Console.WriteLine(data); 
} 
+0

Спасибо за ваше предложение. Он отлично работает, если я модифицирую код в качестве вашего предложения. Однако это структура моего существующего приложения, действие класса на самом деле происходит из отдельного проекта библиотеки, и я хочу упростить код вызывающего абонента, получив строку непосредственно из этого метода. И в моем приложении goDownload вызывается из фонового потока, который запускается событием таймера. Есть ли способ построить функцию утилиты, например goDownload, как указано выше, так что вызывающий может просто использовать этот метод для непосредственного получения строки без добавления async/await. –

+0

@James: Вам нужно было бы вложить все это в фоновый поток, если вы хотите блокировать вызов. Но в принципе вы вызываете метод из потока пользовательского интерфейса на данный момент - и вам действительно не следует блокировать поток пользовательского интерфейса для сетевых операций. –

+0

Большое спасибо. Я просто попытаюсь упростить случай, используя приведенный выше пример, поэтому он вызывает от нажатия кнопки потока пользовательского интерфейса. Я просто нахожу другой пример, кажется, что если бы я получил результат непосредственно из GetAcync (page). Результат, он будет выполнен в режиме синхронизации. Но я не знаю, работает ли он под фоновым потоком. Я буду модифицировать свой проект, чтобы проверить его. –

1

верхнего уровня причина этого - WPF (как любое приложение пользовательского интерфейса) имеют UI-нить, что работа с очередью winwdows цикла. Джон Скит дать вам простейший desigion, но вы можете использовать нить из CLR ThreadPool:

  Task<string> t = Task.Run(() => DownloadPageAsync()); 
     string result = = await t; 
+1

Большое спасибо. Хотя это не совсем то, что я ищу, но это дает мне идею сделать требуемую функцию в другом подходе, и теперь я узнал больше о потоке пользовательского интерфейса WPF. –

+0

Добро пожаловать :-) – Alexander

Смежные вопросы