2014-05-16 172 views
5

我想設計一個瀏覽器,以編程方式獲取網站更新。我試圖用async/await方法做到這一點,但是當我嘗試運行程序時,它似乎只是掛在response.Wait();.不知道爲什麼或什麼發生。異步httpclient操作

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 

     var urls = sourceUrls(); 
     Task<HttpResponseMessage> response = login(urls[0]); 
     response.Wait(); 

     Console.Write("Complete"); 

    } 

    private List<Site> sourceUrls() 
    { 

     var urls = new List<Site>(); 
     urls.Add(new Site("http://yahoo.com", "test", "test")); 

     return urls; 
    } 
} 

瀏覽器類::

static public class Browser 
{ 

    private static CookieContainer cc = new CookieContainer(); 
    private static HttpClientHandler handler = new HttpClientHandler(); 
    public static HttpClient browser = new HttpClient(handler); 

    static Browser() 
    { 
     handler.CookieContainer = cc; 
    } 


    static public async Task<HttpResponseMessage> login(Site site) 
    { 
     var _postData = new Dictionary<string, string> 
     { 
      {"test", "test"} 
     }; 

     FormUrlEncodedContent postData = new FormUrlEncodedContent(_postData); 
     HttpResponseMessage response = await browser.PostAsync(site.url, postData); 
     return response; 
    } 
} 

而且,我的預期目的,是確定以使瀏覽器的靜態函數,或者根本不是什麼意義?對不起,我對c#有新問題。

+0

它會讓你使'MainWindow()''async'方法嗎?如果可以的話,你可以直接等待。我很確定'Task.Wait()'是一個阻塞方法。 – pquest

+0

我的印象是mainwindow()是WPF中的main()等價物,所以它是一個阻塞方法。 – cubesnyc

+0

我編輯了你的標題。請參閱「[應該在標題中是否包含」標籤「](http://meta.stackoverflow.com/questions/19190/)」,其中共識是「不,他們不應該」。 –

回答

1

這是一個壞主意來調用一個TaskWait在UI線程,它會導致死鎖。 你可以簡單地await這個迴應會達到你想要的。 await解開Task的響應,因此您的返回類型現在只是HttpResponseMessage。由於您不能將構造函數標記爲async,因此可以將該功能移至啓動該操作的另一種方法。

public MainWindow() 
{ 
    InitializeComponent(); 
    LoadUrlsAsync(); 
} 

public async Task LoadUrlsAsync() 
{ 
    var urls = sourceUrls(); 
    HttpResponseMessage response = await login(urls[0]); 

    Console.Write("Complete"); 
} 

使用async/await時,請參閱this文章的最佳實踐。

或者,您可以使用Task.ConfigureAwait擴展方法來配置延續不運行在當前SyncrhronizationContext這也應該避免死鎖。

public MainWindow() 
{ 
    InitializeComponent(); 

    var urls = sourceUrls(); 
    Task<HttpResponseMessage> response = login(urls[0]).ConfigureAwait(false); 
    response.Wait(); 

    Console.Write("Complete"); 
} 
+1

主窗口不能聲明爲異步,所以你的第一個例子將不起作用。 – cubesnyc

+0

謝謝,現在修復。 –

0

GetUrl()GetUrl2()(見下)以完全不同的方式完成了您的工作。但請注意,Task.Wait將阻止當前線程(在本例中爲UI線程),直到任務完成。我猜這不是你想要做的。

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

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     GetUrls(); 
     GetUrls2(); 
    } 
    private void GetUrls() 
    { 
     var response = Task.Run(() => Browser.login("http://yahoo.com")); 
     response.Wait(); 
     Debug.WriteLine("Complete GetUrls()"); 
    } 
    private void GetUrls2() 
    { 
     var browseTask = Browser.login("http://yahoo.com"); 
     browseTask.ContinueWith((r) => Debug.WriteLine("Complete GetUrls2()")); 
    } 
} 
static public class Browser 
{ 
    static public async Task<HttpResponseMessage> login(string site) 
    { 
     var browser = new HttpClient(); 
     return await browser.GetAsync(site); 
    } 
} 
1

Wait原因a deadlock that I explain in full on my blog。 Ned建議最好的解決方案是使用await

在你的情況下,構造函數不能是async,所以這是不可能的。這應該表明,你想要做的事情可能不是最好的事情。特別是,你試圖阻止UI線程等待下載完成;這幾乎不是一個好主意。

取而代之,讓您的構造函數啓動異步操作並(同步)加載不顯示數據的視圖 - 例如,「加載」屏幕或空視圖,無論對於問題域最有意義。然後,當異步操作完成時,它應該用新數據更新視圖。我有一篇關於async data binding的文章,探討了一種可能的技術。