PLINQ与任务、异步与生产者/消费者队列?使用什么

本文关键字:队列 什么 消费者 任务 异步 生产者 PLINQ | 更新日期: 2023-09-27 18:21:06

我正在阅读C# 5.0 in nutshell,在阅读了作者的观点后,我很困惑我应该采用什么。我的要求是,假设我有一个非常长的运行(计算量很大)任务,例如,计算数百万文件的SHA1(或其他)哈希,或者任何其他事情都是计算量大,可能需要一些时间,我应该如何开发它(在winforms中,如果重要的话,使用VS 2012,C#5.0),so that I can also report progress to the user

脑海中浮现以下情景。。。

  1. 通过实现IProgess<T>Progess<T>让任务捕获SynchronizationContext上下文并发布到UI,创建一个Task(带有LongRunning选项,用于计算哈希并向用户报告进度)

  2. 创建类似的Async方法

     async CalculateHashesAsync() 
     {
         // await here for tasks the calculate the hash
         await Task.Rung(() => CalculateHash();
        // how do I report progress???
     }
    
  3. 使用TPL(或PLINQ)作为

    void CalcuateHashes()  
    {  
        Parallel.For(0, allFiles.Count, file => calcHash(file)    
        // how do I report progress here?   
    }
    
  4. 使用生产者/消费者队列
    真的不知道怎么做?

书中的作者说。。。

在池线程上运行一个长时间运行的任务不会导致麻烦它是指并行运行多个长时间运行的任务(尤其是那些阻碍的)性能可能会受到影响。因为在这种情况下,通常有比任务创建选项。长期运行

  • 如果任务是IO绑定的,TaskCompletionSource和异步函数允许用回调而不是线程实现并发
  • 如果任务是计算绑定的,则生产者/消费者队列允许您限制这些任务的并发性,避免其他线程和进程

关于Producer/Consumer,作者说。。。

生产者/消费者队列是一种有用的结构,两者并行编程和一般并发场景控制一次执行多少工作线程,这很有用不仅在限制CPU消耗方面而且在其他资源方面也是如此。

所以,我应该不使用task吗,这意味着第一个选项已经过时了?第二个是最好的选择吗?还有其他选择吗?如果我听从作者的建议,实现生产者/消费者,我将如何做到这一点(我甚至不知道在我的场景中如何开始生产者/消费者的工作,如果这是最好的方法!)

我想知道,如果有人遇到过这样的情况,他们会如何实施?如果不是,什么是最有效和/或最容易开发/维护的性能(我知道performance这个词是主观的,但让我们只考虑一个非常普遍的情况,它可以工作,而且工作得很好!)

PLINQ与任务、异步与生产者/消费者队列?使用什么

真正长时间运行(计算量大)的任务,例如,计算数百万文件的SHA1(或其他)哈希

该示例显然同时包含繁重的CPU(哈希)和I/O(文件)组件。也许这是一个不具代表性的例子,但根据我的经验,即使是安全的哈希也比从磁盘读取数据快得多。

如果您的工作仅限于CPU,那么最好的解决方案是Parallel或PLINQ。如果您只有I/O绑定工作,那么最好的解决方案是使用async。如果您有一个更现实、更复杂的场景(同时有CPU和I/O工作),那么您应该将CPU和I/O部件与生产商/消费者队列连接起来,或者使用更完整的解决方案,如TPL数据流。

TPL数据流与并行(MaxDegreeOfParallelism)和async都能很好地工作,并且在每个块之间都有一个内置的生产者/消费者队列。

当混合使用大量I/O和CPU时,需要记住的一件事是,不同的情况可能会导致截然不同的性能特征。为了安全起见,您需要对队列中的数据进行节流,这样就不会出现内存使用问题。TPL Dataflow内置支持通过BoundedCapacity进行节流。