等待成千上万的任务

本文关键字:任务 成千上万 等待 | 更新日期: 2023-09-27 18:13:46

我有一个转换数据的应用程序,通常有1.000 - 30.000个文件。

我需要做3个步骤:

  1. 复制一个文件(替换一些文本)
  2. 用WebClient做一个Webrequest来下载一个文件(我将复制的文件发送到WebServer,它将文件转换为另一种格式)
  3. 获取下载的文件并更改部分内容

所以这三个步骤都包含一些I/O,我使用了async/await方法:

var tasks = files.Select(async (file) =>
{
    Item item = await createtempFile(file).ConfigureAwait(false);
    await convert(item).ConfigureAwait(false);
    await clean(item).ConfigureAwait(false);
}).ToList();
await Task.WhenAll(tasks).ConfigureAwait(false);

我不知道这是否是最佳实践,因为我创建了上千个任务。我想把这三个步骤分成如下三个步骤:

List<Item> items = new List<Item>();
var tasks = files.Select(async (file) =>
{
    Item item = await createtempFile(file, ext).ConfigureAwait(false);
    lock(items)
        items.Add(item);
}).ToList();
await Task.WhenAll(tasks).ConfigureAwait(false);
var tasks = items.Select(async (item) =>
{
    await convert(item, baseAddress, ext).ConfigureAwait(false);
}).ToList();
await Task.WhenAll(tasks).ConfigureAwait(false);
var tasks = items.Select(async (item) =>
{
    await clean(targetFile, item.Doctype, ext).ConfigureAwait(false);
}).ToList();
await Task.WhenAll(tasks).ConfigureAwait(false);

但这似乎并没有更好或更快,因为我创建了3倍的数千个任务。

我应该限制任务的创建吗?比如100个任务?或者我只是想多了,创造成千上万的任务就好了。

CPU空闲,峰值为2-4%,所以我认为等待或上下文切换太多。

也许WebRequest调用太多了,因为WebServer/WebService不能同时处理数千个请求,我应该只节流WebRequest ?

我已经在app.config文件中增加了。net maxconnection

等待成千上万的任务

可以在限制并发操作的数量的情况下并行执行异步操作。有一个很酷的扩展方法,它是不是。net框架的一部分。

/// <summary>
/// Enumerates a collection in parallel and calls an async method on each item. Useful for making 
/// parallel async calls, e.g. independent web requests when the degree of parallelism needs to be
/// limited.
/// </summary>
public static Task ForEachAsync<T>(this IEnumerable<T> source, int degreeOfParalellism, Func<T, Task> action)
{
    return Task.WhenAll(Partitioner.Create(source).GetPartitions(degreeOfParalellism).Select(partition => Task.Run(async () =>
    {
        using (partition)
            while (partition.MoveNext())
                await action(partition.Current);
    })));
}

这样写:

var files = new List<string> {"one", "two", "three"};
await files.ForEachAsync(5, async file =>
{
   // do async stuff here with the file
   await Task.Delay(1000);
});

正如评论者正确指出的那样,你想得太多了。. net运行时跟踪数千个任务绝对没有问题。

但是,您可能想要考虑使用TPL数据流管道,这将使您能够轻松地为管道中的不同操作("块")设置不同的并发级别。