C# 在.NET应用程序中等待1000个任务的集合可以吗?还是应该使用配料?

C# 在.NET应用程序中等待1000个任务的集合可以吗?还是应该使用配料?,c#,.net,azure,async-await,C#,.net,Azure,Async Await,我有一个简单的控制台应用程序来测试一些代码 我的代码有一个1000个数字的列表,并将每个数字/整数放入Azure队列 现在,我异步地做这件事,效果很好。以下是我的库中的代码: var tasks = stagedFileIds.Select(stagedFileId => QueueFileToProcessAsync(stagedFileId)); await Task.WhenAll(tasks) .ConfigureAwait(false); 效果很好 但是。。

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

我的代码有一个1000个数字的列表,并将每个数字/整数放入Azure队列

现在,我异步地做这件事,效果很好。以下是我的库中的代码:

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

但是。。这是件坏事吗?我应该把它分为50个还是25个?但最重要的是。。。批处理

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


记住,现在这是一个控制台应用程序。我将在某个时候将其移动到Azure函数。

如果您的操作被IO阻止,这意味着它们等待某些资源,例如访问文件、返回web请求等,那么这是一个正常的解决方案(尽管要小心异常)。您是否应该批处理它取决于资源。如果所有任务都要写入同一个文件,那么异步将毫无意义。如果所有任务都写入同一个驱动器,那么就可以了。如果调用外部web服务器,这取决于它可以处理多少请求(并了解
System.Net.ServicePointManager.DefaultConnectionLimit


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

您应该以异步方式限制它们,以确保不会并行执行太多QueueFileToProcessAsync操作,除非您确定它是无害的。我建议你,在哪里和他的其他帖子地址节流

如果您正在调用和终结点,则可能会被@Gerino指出的
ServicePointManager.DefaultConnectionLimit
限制

就中俄国际商用飞机有限责任公司而言,如果您必须在没有第三方物流数据流的情况下自行实施,您可以使用:

//原型代码
静态类TaskThrottlingExtension
{
公共静态异步任务ThrottleProcessingAsync(此IEnumerable inputs,int parallel,Func进程)
{
var queues=newblockingcollection[parallel];
var任务=新任务[并行];
for(int i=0;i
{
foreach(队列中的var输入。GetConsumingEnumerable())
{
等待进程(输入)。配置等待(假);
}
});
}
尝试
{
foreach(输入中的var输入)
{
BlockingCollection.AddToAny(队列、输入);
}
foreach(队列中的var队列)
{
queue.CompleteAdding();
}
等待任务.WhenAll(任务).配置等待(false);
}
最后
{
foreach(队列中的var队列)
{
queue.Dispose();
}
}
}
}

它已在线程池中批处理。它不会启动1000个线程。这可能取决于这些任务的作用。如果您在任务中包装线程,那么您刚刚生成了1000个线程,这是不好的。如果将异步I/O包装到任务中,可能会向远程服务器发出1000个请求,这可能也不好。如果您已将1000个使用异步I/O的任务排队,并使用线程池运行其代码,那么这一切可能都很好。但问题中的代码在这方面既不好也不坏。这就是等待1000个任务的方式,问题可能在于任务,而不是此代码。如果使用DOP>1的ActionBlock,您将获得更好的性能和可管理性。至于Azure功能,增加3-4个数量级的延迟并不能改善情况。现在,从一个任务转移到另一个任务,或者从一个块转移到另一个块的延迟可以忽略不计。加上TPL数据流和TPL本身提供了简单的缓冲和节流。这对于任何无服务器平台来说都不是微不足道的。在任何情况下,TPL数据流管道更接近Azure函数管道。到管道的每个TransformBlock都可以成为一个单独的函数等待大量任务没有什么错。几年前,我在
whalll
(使用异步任务)上做了压力测试,给我留下了深刻的印象——你遇到内存问题的时间比遇到
whalll
限制的时间要早()。比旧的等待多个句柄API的
64
要好得多。但是,你可以考虑节制你的请求。你还没有很好地理解这个问题,请检查Lasse的评论,因为他已经提到了排队等候的任务的潜在问题,这取决于他们所完成的任务。这与异步等待与并行无关。也是Panagiotis建议的第三方物流数据流选项
// 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();
            }
        }
    }
}