任务冻结了GUI

时间:2015-11-08 07:17:49

标签: c# winforms task freeze taskfactory

我正在尝试创建一个函数来从多个页面获取源代码。抓取每个页面后,我想更新表单上的标签,指示进度(5个中的1个,5个中的2个等)。

但是,无论我尝试什么,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);

但是,现在我收到了这个错误:

  

'await'运算符只能在异步方法中使用。考虑使用'async'修饰符标记此方法并将其返回类型更改为'Task'。

但是当我用异步标记方法时,GUI仍然会冻结。

更新2

Woops!我似乎错过了达林的一种新方法。我现在已在方法中包含await webGet.LoadAsync(url + i);。我还将我调用的方法标记为async

现在,不幸的是,我收到了这个错误:

  

'HtmlWeb'不包含'LoadAsync'的定义,并且没有可以找到接受“HtmlWeb”类型的第一个参数的扩展方法'LoadAsync'(您是否缺少using指令或汇编引用?)

我已经检查过,我使用的是.NET 4.5.2,而我的References中的HtmlAgilityPack是Net45版本。我不知道现在发生了什么。

2 个答案:

答案 0 :(得分:1)

  

如果我删除task.Wait(); GUI解冻,标签更新   正确,但代码继续没有所需的多维   列表。

这是正常的。您应该更新您的函数,使其不返回值,而是返回任务:

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

然后在调用此函数时,您将在结果上使用ContinueWith:

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.

答案 1 :(得分:0)

假设WinForms,首先制作toplevel eventhandler async void

然后,您可以使用await 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;
}