LINQ AsParallel.Select(SynchronousAction) 与 Task.Run(SynchronousAction) 的优点

Advantages of LINQ AsParallel.Select(SynchronousAction) versus Task.Run(SynchronousAction)

提问人:Kevin 提问时间:4/2/2023 更新时间:4/2/2023 访问量:51

问:

这个问题是关于如何以 async/await 或 Task.Run() 方式处理第三方同步文件读取方法。我有一个包含 50+ 传入路径名的列表,我用它们来处理以使用更多线程。但是,可以想象,语句中的所有线程在进行同步工作时可能会阻塞并等待一小段时间。这是我应该担心的事情吗?.AsParallelAsParallelPathReadAndProcessOne(path)

某些线程(我的线程或线程池线程)必须运行并等待第三方文件处理器完成。我的调用线程除了等待处理路径名外别无他法,因此我不确定是否使用异步来释放我的调用线程是否重要。

LINQ+ 同步代码和下面的 Task.Run+ 异步代码的运行时间大致相同。

以一种或另一种方式编写代码是否有概念或未来优势?

// the LINQ+synchronous version - only one line of code
var nestedActions = paths.AsParallel().Select(PathReadAndProcessOne).ToList();


// The Task.Run() async version - ASYNC ALL THE WAY DOWN
// 
// loop over all paths and kick off a Task.Run for each one.
var allTasks = new List<Task<List<ActionItem>>>();
foreach (var path in paths) {
  var t1 = Task.Run(() => PathReadAndProcessOne(path));
  allTasks.Add(t1);
}

// after all tasks are started in the loop, await them all outside the loop
try {
  Task.WaitAll();
}
catch {
  return false;
}

// pull out the results and aggregate them
var nestedActions = new List<ActionItem>();
foreach (var t in allTasks) {
  nestedActions.AddRange(t.Result);
}
多线程 LINQ 异步 任务并行库

评论

0赞 Panagiotis Kanavos 4/3/2023
选项 3 更好 - 数据流或通道。PLINQ/Parallel.For 用于内存中数据并行,而不是异步操作。他们将输入数据拆分为多个分区,每个分区使用一个工作线程,以最大程度地降低同步成本。这里的情况并非如此。您可以使用 Dataflow 管道分多个步骤处理文件数据,每个步骤都在自己的“worker”上运行,例如抓取文件夹、解析文件、将它们导入数据库。您可以调整每个步骤的工作线程数量,例如增加并发解析器。
0赞 Panagiotis Kanavos 4/3/2023
在这种情况下,您希望限制一次处理的文件数。不能在 4 核计算机上同时处理 50 个文件。某些任务将不得不等待,占用缓冲区和 RAM。您最终可能会在 RAM 中加载 50 个文件,但只有 4 个线程在主动处理它们
0赞 Kevin 4/4/2023
谢谢。我将不得不查找 Dataflow 和 Channels - 我从未听说过它们。

答:

1赞 Stephen Cleary 4/2/2023 #1

通常,我建议在有多个任务时使用 或 PLINQ()。 低一个抽象级别,将任务排队到线程池中。 PLINQ 具有分区逻辑,用于将工作调度到一组任务上,而这些任务是调度到线程池上的任务。因此,特别是对于大型工作负载,出于这个原因,我推荐 / PLINQ。在 PLINQ 之间,如果您的操作有结果,则 PLINQ 更易于使用,而您的操作也是如此。ParallelAsParallelTask.RunParallelParallelParallel

我的调用线程除了等待处理路径名外别无他法,因此我不确定是否使用异步来释放我的调用线程是否重要。

不,在这种情况下没关系。如果您最终在异步很重要的地方(即从 GUI 线程)使用它,那么您可以将并行代码包装在一个 .Task.Run

评论

1赞 Kevin 4/2/2023
很好的答案 - 谢谢!(哈哈,就像你以前做过一样......我刚刚发现了你所有的作品,正在疯狂地阅读以了解一切!