技术频道导航
HTML/CSS
.NET技术
IIS技术
PHP技术
Js/JQuery
Photoshop
Fireworks
服务器技术
操作系统
网站运营

赞助商

分类目录

赞助商

最新文章

搜索

实例显示C# Task.WhenAll与Parallel.ForEach的使用差异

作者:admin    时间:2023-6-7 11:42:11    浏览:

C#里 Task.WhenAllParallel.ForEach 都是实现多个并发任务执行的一种方式。但是有一些不同之处,这些差异指定了它们各自适合的使用位置。

在比较它们差异之前,让我们先简单了解一下 Task.WhenAllParallel.ForEach 的概述。

Task.WhenAll 方法

定义

  • 命名空间:System.Threading.Tasks
  • 集合:System.Runtime.dll

创建一个任务,该任务将在所有提供的任务完成后完成。

重载

WhenAll(IEnumerable<Task>) 创建一个任务,该任务将在可枚举集合中的所有Task对象完成时完成。
WhenAll(Task[]) 创建一个任务,该任务将在数组中的所有Task对象完成时完成。
WhenAll<TResult>
(IEnumerable<Task<TResult>>)
创建一个任务,该任务将在可枚举集合中的所有Task<TResult>对象完成时完成。
WhenAll<TResult>(Task<TResult>[]) 创建一个任务,该任务将在数组中的所有Task<TResult>对象完成时完成。

Parallel.ForEach 方法

定义

  • 命名空间:System.Threading.Tasks
  • 集合:System.Threading.Tasks.Parallel.dll

执行一个foreach(Visual Basic 中的 For Each )操作,其中迭代可以并行运行。

重载

ForEach<TSource,TLocal>(IEnumerable<TSource>,
ParallelOptions, Func<TLocal>,
Func<TSource,ParallelLoopState,TLocal,TLocal>,
Action<TLocal>)
使用IEnumerable上的线程本地数据执行foreach(Visual Basic 中的For Each)操作,其中迭代可以并行运行,可以配置循环选项,并且可以监视和操作循环的状态。
ForEach<TSource,TLocal>(IEnumerable<TSource>,
ParallelOptions, Func<TLocal>,
Func<TSource,ParallelLoopState,Int64,TLocal,TLocal>,
Action<TLocal>)
在IEnumerable上使用线程本地数据和 64 位索引执行foreach(Visual Basic 中的For Each)操作,其中迭代可以并行运行,可以配置循环选项,并且可以监视和操作循环的状态。
ForEach<TSource,TLocal>(IEnumerable<TSource>,
Func<TLocal>,
Func<TSource,ParallelLoopState,TLocal,TLocal>,
Action<TLocal>)
使用IEnumerable上的线程本地数据执行foreach(在Visual Basic 中的For Each)操作,其中迭代可以并行运行,并且可以监视和操作循环的状态。
ForEach<TSource,TLocal>(IEnumerable<TSource>,
Func<TLocal>,
Func<TSource,ParallelLoopState,Int64,TLocal,TLocal>,
Action<TLocal>)
使用IEnumerable上的线程本地数据执行foreach(Visual Basic 中的For Each)操作,其中迭代可以并行运行,并且可以监视和操作循环的状态。

Task.WhenAll与Parallel.ForEach的差异

我想从一个示例开始来描述Task.WhenAllParallel.ForEach的差异。假设我们有一个方法,该方法由一个 HTTP 调用和一个存储在数据库中的命令组成。我们想同时执行这个请求的多个任务。

public async Task Foo(Request request)
{            
    var result = await httpService.Send(request);
           
    await repository.Store(result);
}

因此,为了同时执行 Foo 方法的多个调用,我们可以在 .Net Core 中使用这些方法:

Parallel.ForEach(requests, async request =>
{
    using (var sendScope = service.CreateScope())
    {
        var callService = sendScope.ServiceProvider.GetRequiredService<ICallService>();
        await callService.Foo(request);
    }
});

此外,可以使用 Task.WhenAll 来执行此操作:

var tasks = requests.Select(async request =>
{
    using (var sendScope = service.CreateScope())
    {
        var callService = sendScope.ServiceProvider.GetRequiredService<ICallService>();
        await callService.Call(request);
    }
});
await Task.WhenAll(tasks);

我已经在我的笔记本电脑上执行了它们,我从不同的执行中捕获了这些结果:

 Task.WhenAll与Parallel.ForEach的执行速度比较
左侧每个数字 100–15000 显示并发任务数

上图显示了 Foo 方法指定数量的并行任务执行需要多长时间,以毫秒为单位。

上面的执行结果有两个重点:

  • 首先,与 Task.WhenAll 相比,Parallel.ForEach 的执行时间更快。
  • 第二点是当并发任务超过8000时,Parallel.ForEach遇到失败状态。

结论

总而言之,虽然 Parallel.ForEach 具有更快的执行时间,但 Task.WhenAll 具有更高的可扩展性。这意味着通过增加请求负载,Task.WhenAll 可以毫无故障地处理它。

不要混淆可扩展性和速度,虽然 Parallel.ForEach 在速度上更好,但与 Task.WhenAll 相比可扩展性较低,无法响应高负载的并发请求。

这个结果来自于 Task.WhenAll 旨在处理具有更高可扩展性的并发 I/O 绑定任务,因为它使用异步非阻塞方式共享线程来处理并发请求。

但是,另一方面,Parallel 本身是同步的。因此,在 CPU 绑定逻辑中使用它以获得更好的性能是有益的。

相关文章

x
  • 站长推荐
/* 左侧显示文章内容目录 */