限制异步任务的频率?

6

我想知道如果待完成的任务数量很大,我们是否应该对异步任务进行节流。比如你有1000个URL,你是一次性发出所有请求然后等待全部完成呢?

var tasks = urlList.Select(url => downloadAsync(url));
await Task.WhenAll(tasks);

或者你将请求分批处理,一个批次接一个批次地处理:
foreach (var urlBatch in urlList.BatchEnumerable(BatchSize)){
    var tasks = urlBatch.Select(url => downloadAsync(url));
    await Task.WhenAll(tasks);
}

我曾认为批处理并非必要,因为第一种方法(一次性发送所有请求)会创建由ThreadPool调度的任务,所以我们应该让ThreadPool决定何时执行每个任务。然而,有人告诉我,实际上这只在任务是计算任务时有效。当任务涉及网络请求时,第一种方法可能会导致主机挂起。为什么会这样?

1个回答

9
在大多数情况下,您需要限制自己的操作。当您有多个操作同时运行时,通常会在某个地方保存一些状态。如果它们是CPU绑定的,则任务存储在ThreadPool队列中等待线程,如果是异步的,则状态机坐落在堆上。
即使是异步操作,通常也会使用一些有限的资源,无论是带宽、端口、远程数据库服务器的CPU等。
您不必一次只处理一个批次(因为需要等待上一个操作完成而不能启动其他操作)。您可以使用SlimSemahpore或更好的TPL Dataflow块进行节流:
var block = new ActionBlock<string>(
   url => downloadAsync(url),
   new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 10 });    

urlList.ForEach(url => block.Post(url));

block.Complete();
await block.Completion;

2
阅读这篇文章对我的并行编程来说是一次革命性的体验。谢谢,伙计! - pim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接