2015-11-08 184 views
0

我想創建一個函數,從多個頁面獲取源代碼。在抓取每個頁面後,我想更新表單上的標籤以指示進度(5中的1個,5中的2個等)。任務凍結GUI

但是,無論我嘗試什麼,GUI完全凍結,直到for循環結束。

public List<List<string>> GetPages(string base_url, int num_pages) 
{ 
    var pages = new List<List<string>>(); 
    var webGet = new HtmlWeb(); 
    var task = Task.Factory.StartNew(() => { 
     for (int i = 0; i <= num_pages; i++) 
     { 
      UpdateMessage("Fetching page " + i + " of " + num_pages + "."); 
      var page = new List<string>(); 
      var page_source = webGet.Load(url+i); 
      // (...) 
      page.Add(url+i); 
      page.Add(source); 
      pages.Add(page); 
     } 
    }); 
    task.Wait(); 
    return pages; 
} 

這個方法的調用看起來是這樣的:

List<List<string>> pages = site.GetPages(url, num_pages); 

如果我刪除task.Wait();的GUI解凍,標籤更新正常,但代碼繼續,而不需要的多維列表。

我應該說我對C#很陌生。我做錯了什麼?

更新

按照達林,我已經改變了我的方法:

public async Task<List<List<string>>> GetPages(string url, int num_pages) 
{ 
    var pages = new List<List<string>>(); 
    var webGet = new HtmlWeb(); 
    for (int i = 0; i <= num_pages; i++) 
    { 
     UpdateMessage("Fetching page " + i + " of " + num_pages + "."); 
     var page = new List<string>(); 
     var page_source = webGet.Load(url+i); 
     // (...) 
     page.Add(url+i); 
     page.Add(source); 
     pages.Add(page); 
    } 
    return pages; 
} 

和呼叫:

List<List<string>> pages = await site.GetPages(url, num_pages); 

不過,現在我得到這個錯誤:

The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.

但是,當我用async標記方法時,GUI仍然凍結。

更新2

Woops!我似乎錯過了達林的一種新方法。我現在在該方法中包含await webGet.LoadAsync(url + i);。我還標記了我從async撥打的方法。

現在,不幸的是,我得到這個錯誤:

'HtmlWeb' does not contain a definition for 'LoadAsync' and no extension method 'LoadAsync' accepting a first argument of type 'HtmlWeb' could be found (are you missing a using directive or an assembly reference?)

我檢查,我使用.NET 4.5.2,在我引用HtmlAgilityPack是Net45的版本。我不知道現在發生了什麼事。

+0

有一個更新UI的單個線程。如果你用'task.Wait()'阻止該線程,那麼UI不能更新。 – Enigmativity

+0

你錯過了達林方法中最重要的部分:「等待webGet.LoadAsync(url + i)'。僅僅因爲一個方法被標記爲'async'並不會自動使它異步。如果您不等待任何事情,它將同步運行並凍結UI。 – sstan

+0

WinForms。我應該使用WPF嗎? – zuddsy

回答

0

假設的WinForms,通過使頂層的事件處理程序async void開始。

然後您可以使用異步方法,該方法可以使用await a Task<List<List<string>>>方法。該方法本身不一定是async

private async void Button1_Click(...) 
{ 
    var pages = await GetPages(...); 
    // update the UI here 
} 


public Task<List<List<string>>> GetPages(string url, int num_pages) 
{ 
    ... 
    return task; 
} 
+0

非常感謝!我終於搞定了。 – zuddsy

1

If I remove task.Wait(); the GUI unfreezes, the label updates properly, but the code continues without the needed multidimensional list.

這很正常。應當更新功能,因此,它不返回值,而是任務:調用此功能,您將使用ContinueWith的結果時

public Task<List<List<string>>> GetPages(string base_url, int num_pages) 
{ 
    var webGet = new HtmlWeb(); 
    var task = Task.Factory.StartNew(() => 
    { 
     var pages = new List<List<string>>(); 
     for (int i = 0; i <= num_pages; i++) 
     { 
      UpdateMessage("Fetching page " + i + " of " + num_chapters + "."); 
      var page = new List<string>(); 
      var page_source = webGet.Load(url+i); 
      // (...) 
      page.Add(url+i); 
      page.Add(source); 
      pages.Add(page); 
     } 
     return pages; 
    }); 

    return task; 
} 

然後:顯然前

var task = GetPages(baseUrl, numPages); 
task.ContinueWith(t => 
{ 
    List<List<string>> chapters = t.Result; 
    // Do something with the results here 
}); 

在繼續訪問t.Result,您可能希望首先檢查其他屬性,以查看任務是否已成功完成,或者是否拋出了某個異常,以便您可以採取相應措施。

此外,如果您使用.NET 4。5,你可能會考慮採取async/await constructs的優勢:

public async Task<List<List<string>>> GetPages(string base_url, int num_pages) 
{ 
    var webGet = new HtmlWeb(); 
    var pages = new List<List<string>>(); 
    for (int i = 0; i <= num_pages; i++) 
    { 
     UpdateMessage("Fetching page " + i + " of " + num_chapters + "."); 
     var page = new List<string>(); 
     var page_source = await webGet.LoadAsync(url+i); 
     // (...) 
     page.Add(url+i); 
     page.Add(source); 
     pages.Add(page); 
    } 
    return pages; 
} 

然後:

List<List<string>> chapters = await GetPages(baseUrl, numPages); 
// Do something with the results here.