我正在尝试创建一个函数来从多个页面获取源代码。抓取每个页面后,我想更新表单上的标签,指示进度(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版本。我不知道现在发生了什么。
答案 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;
}