可以在.NET应用程序中等待1000个任务的集合吗?或者应该使用批处理?

时间:2017-04-10 08:56:13

标签: c# .net azure async-await

我有一个简单的控制台应用程序测试一些代码。

我的代码有一个包含1000个数字的列表,并将每个数字/ int放到Azure队列中。

现在,我这样做是异步的,而且效果很好。这是我库中的代码:

var tasks = stagedFileIds.Select(stagedFileId => QueueFileToProcessAsync(stagedFileId));
await Task.WhenAll(tasks)
          .ConfigureAwait(false);

效果很好。

但是......这是件坏事吗?我应该把它分成50或25或者什么?但最重要的是......批量吗?

执行上述代码的“成本”是多少?

请记住,这是一个控制台应用现在。我将在某个时候将其移动到Azure功能。

2 个答案:

答案 0 :(得分:2)

您应该以异步方式限制它们,以确保您没有并行地进行太多的QueueFileToProcessAsync操作,除非您确定它是无害的。我建议你Stephen Cleary introduction to TPL Dataflow,其中part 3和他的其他帖Async Producer/Consumer Queue using Dataflow地址限制。

如果你正在调用和端点,那么@Gerino指出的ServicePointManager.DefaultConnectionLimit可能会受到限制。

只是为了这个小问题,如果你必须在没有TPL数据流的情况下自己实现,你可以使用.NET Concurrent Collections

// prototype code
static class TaskThrottlingExtension
{
    public static async Task ThrottleProcessingAsync<T>(this IEnumerable<T> inputs, int parallel, Func<T, Task> process)
    {
        var queues = new BlockingCollection<T>[parallel];
        var tasks = new Task[parallel];
        for (int i = 0; i < parallel; i++)
        {
            var queue = queues[i] = new BlockingCollection<T>(1);
            tasks[i] = Task.Run( async () =>
            {
                foreach (var input in queue.GetConsumingEnumerable())
                {
                    await process(input).ConfigureAwait(false);
                }
            });
        }

        try
        {
            foreach (var input in inputs)
            {
                BlockingCollection<T>.AddToAny(queues, input);
            }

            foreach (var queue in queues)
            {
                queue.CompleteAdding();
            }

            await Task.WhenAll(tasks).ConfigureAwait(false);
        }
        finally
        {
            foreach (var queue in queues)
            {
                queue.Dispose();
            }
        }
    }
}

答案 1 :(得分:0)

如果您的操作是IO阻止的,这意味着他们等待某些资源,无论是访问文件,还是返回Web请求等,那么这是一个好的解决方案(但要小心例外)。是否应该批量处理它取决于资源。如果所有任务都想写入同一个文件,那么完全异步就没有意义。如果所有任务都写入同一个驱动器 - 那么它可能没问题。如果您呼叫外部Web服务器,则取决于它可以处理多少请求(并阅读System.Net.ServicePointManager.DefaultConnectionLimit

如果您的操作是CPU阻塞的,这意味着他们需要在本地计算机上执行一些繁重的计算,数据处理,那么更好的方法是Parallel.ForEach。这将自动获取集合并在特定数量的线程(您可以使用DegreesOfParallelism选项指定)之间划分工作。如果你有4个HT内核,那么8个逻辑线程,你可以将DOP设置为8,并且你的所有CPU都将完全用于尽快完成处理。完成所有项目后,该方法将返回(或者您使用CancellationToken取消了该项目。)