C# 等待成千上万的任务
我有一个应用程序,它可以转换一些数据,通常有1.000-30.000个文件 我需要执行3个步骤:C# 等待成千上万的任务,c#,asynchronous,async-await,C#,Asynchronous,Async Await,我有一个应用程序,它可以转换一些数据,通常有1.000-30.000个文件 我需要执行3个步骤: 复制文件(替换其中的一些文本) 使用WebClient发出Webrequest以下载文件(我将复制的文件发送到Web服务器,该服务器将文件转换为其他格式) 获取下载的文件并更改部分内容 因此,这三个步骤都包括一些I/O,我使用了异步/等待方法: var tasks = files.Select(async (file) => { Item item = await createtemp
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);
List items=newlist();
var任务=文件。选择(异步(文件)=>
{
Item Item=await createtempFile(文件,ext).ConfigureAwait(false);
锁(项目)
项目。添加(项目);
}).ToList();
等待任务.WhenAll(任务).配置等待(false);
变量任务=项目。选择(异步(项目)=>
{
等待转换(项、基地址、分机)。配置等待(false);
}).ToList();
等待任务.WhenAll(任务).配置等待(false);
变量任务=项目。选择(异步(项目)=>
{
等待清除(targetFile,item.Doctype,ext).ConfigureAwait(false);
}).ToList();
等待任务.WhenAll(任务).配置等待(false);
但这似乎不是更好或更快,因为我创建了3倍于数千个任务
我应该限制任务的创建吗?比如100个任务的块?
或者我只是想得太多了,而创建数千个任务也很好
CPU以2-4%的峰值空闲,所以我考虑了太多的等待或上下文切换
也许WebRequest调用太多了,因为WebServer/WebService不能同时处理数千个请求,我应该只限制WebRequest
我已经在app.config文件中增加了.NET maxconnection。可以并行执行异步操作,同时限制并发操作的数量。有一种很酷的扩展方法,它不是.Net framework的一部分
/// <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);
})));
}
//
///并行枚举集合并对每个项调用异步方法。对制作有用
///并行异步调用,例如,需要调整并行度时的独立web请求
///有限的。
///
公共静态任务ForEachAsync(此IEnumerable源代码,int degreeOfParallelism,Func操作)
{
返回Task.WhenAll(Partitioner.Create(source).GetPartitions(degreeofparallelism).Select(partition=>Task.Run(async()=>
{
使用(分区)
while(partition.MoveNext())
等待操作(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);
});
var files=新列表{“一”、“二”、“三”};
等待文件。ForEachAsync(5,异步文件=>
{
//在这里对文件执行异步操作
等待任务。延迟(1000);
});
正如评论者正确指出的那样,你想得太多了。NET运行时在跟踪数千个任务方面绝对没有问题
但是,您可能需要考虑使用TPL数据流管道,这将使您可以容易地为不同的操作(“块”)在流水线中具有不同的并发级别。< /P>什么问题?您可以使用<代码>并行。Parallel.ForEach包含分区器,以避免执行超出必要范围的任务<代码>等待并不意味着启动线程并并行工作。所以我认为你必须重新思考你的想法,使它成为一个真正的线程化应用程序。@NiyokoYuliawan Parallel.Foreach用于CPU绑定的工作,而不是IO绑定的工作。是的,你想得太多了。任务不是线程。它们是小而便宜的包装,而“千”是微不足道的。谢谢你,我不知道这种实现。我总是用信号量lim来限制我的工作。