我有一个简单的控制台应用程序测试一些代码。
我的代码有一个包含1000个数字的列表,并将每个数字/ int放到Azure队列中。
现在,我这样做是异步的,而且效果很好。这是我库中的代码:
var tasks = stagedFileIds.Select(stagedFileId => QueueFileToProcessAsync(stagedFileId));
await Task.WhenAll(tasks)
.ConfigureAwait(false);
效果很好。
但是......这是件坏事吗?我应该把它分成50或25或者什么?但最重要的是......批量吗?
执行上述代码的“成本”是多少?
请记住,这是一个控制台应用现在。我将在某个时候将其移动到Azure功能。
答案 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
取消了该项目。)