等待成千上万的任务

时间:2016-10-19 07:02:05

标签: c# asynchronous async-await

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

我需要做3个步骤:

  1. 复制文件(替换其中的一些文字)
  2. 使用WebClient创建Webrequest以下载文件(我将复制的文件发送到WebServer,将文件转换为其他格式)
  3. 获取下载的文件并更改部分内容
  4. 所以这三个步骤都包含一些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);
    

    但这似乎没有更好或更快,因为我创造了数千次任务。

    我应该限制任务的创建吗?像100个任务的大块? 或者我只是过度思考它,创造成千上万的任务就好了。

    CPU处于空闲状态,峰值为2-4%,所以我想到了太多的等待或上下文切换。

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

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

2 个答案:

答案 0 :(得分:6)

可以并行执行并行异步操作限制并发操作数。有一种很酷的扩展方法,不是.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);
});

答案 1 :(得分:3)

正如评论者正确指出的那样,你是在思考它。 .NET运行时在跟踪数千个任务时绝对没有问题。

但是,您可能需要考虑使用TPL Dataflow管道,这样您就可以轻松地为管道中的不同操作(“块”)提供不同的并发级别。