0

我正在尝试创建一个函数来从多个页面获取源代码。抓取每一页后,我想更新表单上的标签,指示进度(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' 运算符只能在异步方法中使用。考虑使用“异步”修饰符标记此方法并将其返回类型更改为“任务”。

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

更新 2

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

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

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

我检查过,我使用的是 .NET 4.5.2,我的参考资料中的 HtmlAgilityPack 是 Net45 版本。我不知道现在发生了什么。

4

2 回答 2

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 结构

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.
于 2015-11-08T07:22:05.200 回答
0

假设 WinForms,首先制作顶级事件处理程序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;
}
于 2015-11-08T08:37:32.007 回答