0

我需要编写一个方法,可以异步加载和解析多个网站。 这是我的方法的简化代码。RanToCompletion仍然在等待时

using (HTMLParser parser = new HTMLParser(proxy)) 
{ 
    var tasks = totalSites.Select(s => 
    { 
     return new Task(async() => 
     { 
      s.Entity = await parser.GetSiteDataAsync(s.Entity).ConfigureAwait(false); 
     }); 
    }).ToArray(); 
    foreach (var task in tasks) 
     task.Start(); 
    await Task.WhenAll(tasks).ConfigureAwait(false); 
} 

HTMLParser类使用HttpClient加载网站并配置它脱手。完整的代码也使用CancellationToken来取消,并使用SemaphoreSlim来降低并行度。

问题是,当任务开始等待分析数据时,它的状态设置为RanToCompletion。然后程序通过Task.WhenAll并配置HTMLParser导致OperationCanceledExceptionHttpClient中。

+0

如果你的代码将被I/O绑定,你的方法通常不应该明确地创建新的Task对象。我认为你现在的代码最后是'Task's包装其他'Task's,这不是盛大的。 –

+0

@GWigWam,对不起,我错过了简化我的代码。 's'用于创建'vm'。 @Damien_The_Unbeliever我认为你是对的:我需要创建多个任务来加载它并行的多个网站。 –

+0

@AndreyAlonzov - 但是对于I/O绑定的工作,你*不应该创建任务 - 你应该利用'async'和'await',但是唯一的你应该处理的任务是通过'async'方法返回的*。 –

回答

4

就像我在评论中所说的,你不应该明确地创建Task s。我想你需要的是沿着线的东西:

using (HTMLParser parser = new HTMLParser(proxy)) 
{ 
    var tasks = totalSites.Select(s => populateEntity(s)).ToArray(); 
    /* returned tasks are already hot */ 
    //foreach (var task in tasks) 
    // task.Start(); 
    await Task.WhenAll(tasks).ConfigureAwait(false); 
} 

,然后分别:

public async Task<WhateverSIs> populateEntity(WhateverSIs s) 
{ 
    s.Entity = await parser.GetSiteDataAsync(s.Entity).ConfigureAwait(false); 
    return s; 
} 

我们没有创建任何Tasks明确 - 我们只是async方法,包括使用那些暴露GetSiteDataAsync

+0

谢谢!我自己也做了同样的事情。昨天我尝试了这种方法,但是我写了lambda中的所有逻辑,并且取消不起作用,并迫使我明确地创建任务。 –

+0

@AndreyAlonzov - 异步lambdas的问题是他们可以很容易地生成'Func'或'Action'委托 - 而'Action'有效'async void'。如果你看看[你通过lambda的地方](https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.task(v = vs.110).aspx)你会发现它只能采用一个“Action” - 所以你无法跟踪你的lambda的进度。 –

相关问题