任务冻结GUI
我想创建一个函数,从多个页面获取源代码。在抓取每个页面后,我想更新表单上的标签以指示进度(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的版本。我不知道现在发生了什么事。
假设的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;
}
非常感谢!我终于搞定了。 – zuddsy
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.
有一个更新UI的单个线程。如果你用'task.Wait()'阻止该线程,那么UI不能更新。 – Enigmativity
你错过了达林方法中最重要的部分:“等待webGet.LoadAsync(url + i)'。仅仅因为一个方法被标记为'async'并不会自动使它异步。如果您不等待任何事情,它将同步运行并冻结UI。 – sstan
WinForms。我应该使用WPF吗? – zuddsy