任务冻结 GUI
Task freezes the GUI
我正在尝试创建一个函数以从多个页面获取源代码。抓取每个页面后,我想更新表单上的标签以指示进度(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);
但是,现在我收到这个错误:
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'.
但是当我用异步标记方法时,GUI 仍然冻结。
更新 2
糟糕!我似乎错过了Darin的新方法。我现在在方法中包含 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,我的References里面的HtmlAgilityPack是Net45版本的。我不知道现在发生了什么。
If I remove task.Wait(); the GUI unfreezes, the label updates
properly, but the code continues without the needed multidimensional
list.
这很正常。您应该更新您的函数,使其不再 return 值而是任务:
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.
假设使用 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;
}
我正在尝试创建一个函数以从多个页面获取源代码。抓取每个页面后,我想更新表单上的标签以指示进度(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);
但是,现在我收到这个错误:
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'.
但是当我用异步标记方法时,GUI 仍然冻结。
更新 2
糟糕!我似乎错过了Darin的新方法。我现在在方法中包含 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,我的References里面的HtmlAgilityPack是Net45版本的。我不知道现在发生了什么。
If I remove task.Wait(); the GUI unfreezes, the label updates properly, but the code continues without the needed multidimensional list.
这很正常。您应该更新您的函数,使其不再 return 值而是任务:
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.
假设使用 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;
}