async / await和Task.Run - 如何知道什么时候完成

时间:2013-01-11 15:56:38

标签: asynchronous task async-await

我的实际程序比这更复杂,但我试图简化一些事情。

所以,假设我正在阅读一个包含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是一种可能性,但我认为它有相同的问题。

2 个答案:

答案 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只会报告其中一个错误。