2015-11-08 147 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.