我的实际程序比这更复杂,但我试图简化一些事情。
所以,假设我正在阅读一个包含URL列表的文件。我想从每个URL下载HTML并进行处理。处理可能有点复杂,所以我希望它可以在一个单独的线程上完成。
基本问题是如何判断所有处理完成的时间。例如,如果用户在处理所有URL之前尝试关闭程序,我想给他一条消息而不退出该程序。或者,我想在处理完所有URL后立即终止程序(可能带有MsgBox(“完成”)消息。)
我希望我的代码看起来如下(假设我有一个外部循环读取URL并调用此例程)...
List<Task> TaskList = new List<Task>();
async void ProcessSingleUrl(string url) {
var web = new HttpClient();
var WebPageContents = await web.GetStringAsync(url);
Task t = Task.Run(() => ProcessWebPage(WebPageContents);
TaskList.Add(t);
}
上面的代码应该运行得非常快(异步方法立即运行得非常好)并几乎立即返回给调用者。
但是在那一点上,我可能在TaskList中没有任何条目,因为在GetStringAsync完成之前没有定义任务,并且当时没有(或者可能只是几个)完成。所以
Task.WaitAll(TaskList.ToArray());
不能按我需要的方式工作。
如果绝对必要,我可以先阅读所有网址并了解预期会有多少任务,但我希望有更优雅的解决方案。
我想我可以在等待之前增加一个计数器,但这感觉有点笨拙。
我认为我的结构不正确,但我不确定如何重组事情。
注意:我没有和Task.Run结合。好的'QueueWorkItem是一种可能性,但我认为它有相同的问题。
答案 0 :(得分:1)
我认为我的结构不正确,但我不确定如何重组事情。
我认为这是真的。这是一个可能的解决方案:将整个计算存储为列表中的Task
,而不仅仅是第二部分:
async Task ProcessSingleUrlInner(string url) {
var web = new HttpClient();
var WebPageContents = await web.GetStringAsync(url);
Task t = Task.Run(() => ProcessWebPage(WebPageContents);
await t;
}
void ProcessSingleUrl(string url) {
var t = ProcessSingleUrlInner(url);
TaskList.Add(t);
}
等待此列表中的所有任务将保证一切都已完成。也许,你需要根据你的确切需求调整这个想法。
答案 1 :(得分:0)
我假设您将网址列表作为IEnumerable<string>
或其他类似网址。
您可以使用LINQ将每个网址转换为Task
,然后将await
全部转换为完成:
async Task ProcessUrls(IEnumerable<string> urls)
{
var tasks = urls.Select(async url =>
{
var web = new HttpClient();
var WebPageContents = await web.GetStringAsync(url);
await Task.Run(() => ProcessWebPage(WebPageContents);
});
await Task.WhenAll(tasks);
}
请注意,如果您使用此解决方案且有多个不同的网址存在错误,则Task.WhenAll
只会报告其中一个错误。