Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/338.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 适用于数百名消费者和大文件的好方法_C#_.net_Multithreading_Task Parallel Library_Tpl Dataflow - Fatal编程技术网

C# 适用于数百名消费者和大文件的好方法

C# 适用于数百名消费者和大文件的好方法,c#,.net,multithreading,task-parallel-library,tpl-dataflow,C#,.net,Multithreading,Task Parallel Library,Tpl Dataflow,我有几个包含数据的文件(每个文件将近1GB)。数据是一个字符串行 我需要与数百名消费者一起处理这些文件。这些消费者中的每一个都会进行一些不同于其他消费者的处理。消费者不会同时在任何地方写东西。它们只需要输入字符串。处理后,它们更新本地缓冲区。消费者可以轻松地并行执行 重要提示:对于一个特定的文件,每个使用者必须按照正确的顺序(如文件中所示)处理所有行(不跳过)。处理不同文件的顺序并不重要 一个消费者处理一条生产线的速度相对较快。我预计Corei5上的时间不到50微秒 所以现在我在寻找解决这个问题

我有几个包含数据的文件(每个文件将近1GB)。数据是一个字符串行

我需要与数百名消费者一起处理这些文件。这些消费者中的每一个都会进行一些不同于其他消费者的处理。消费者不会同时在任何地方写东西。它们只需要输入字符串。处理后,它们更新本地缓冲区。消费者可以轻松地并行执行

重要提示:对于一个特定的文件,每个使用者必须按照正确的顺序(如文件中所示)处理所有行(不跳过)。处理不同文件的顺序并不重要

一个消费者处理一条生产线的速度相对较快。我预计Corei5上的时间不到50微秒

所以现在我在寻找解决这个问题的好方法。 这将是.NET项目的一部分,所以请让我们只使用.NET(最好是C#)

我知道第三方物流和数据流。我想最相关的应该是
BroadcastBlock
。但我认为这里的问题是,每一行我都必须等待所有消费者完成,才能发布新的一行。我想这不是很有效率

我认为理想情况是这样的:

  • 一个线程读取文件并写入缓冲区
  • 每个使用者在准备就绪时,都会并发地从缓冲区读取该行并对其进行处理
  • 当一个消费者读取缓冲区中的条目时,不应将其删除。只有当所有消费者都已处理它时,才能将其删除
  • TPL自行安排使用者线程
  • 如果一个消费者的表现优于其他消费者,那么它不应该等待,可以从缓冲区中读取更多最近的条目

  • 我这样做对吗?无论是不是,我如何实现好的解决方案?

    我不同意一个线程从文件读取并写入缓冲区
    在多个1 GB的文件中,该线程将消耗太多内存
    .NET有对象大小限制,集合是一个对象

    您需要限制阅读线路
    我想你可以用BlockingCollection来做到这一点 不列颠哥伦比亚省的1000000名消费者致力于让最慢的消费者保持忙碌
    它还为打开下一个文件提供了一些缓冲区

    using System.IO;
    using System.Collections.Concurrent;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace BlockingCollection2
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
            public static void BC_GetConsumingEnumerableCollection()
            {
                List<string> fileNames = new List<string>();  // add filesNames
                string producerLine;
                System.IO.StreamReader file;
                List<BCtaskBC> bcs = new List<BCtaskBC>();  // add for each consumer
                // Kick off a producer task
                Task.Factory.StartNew(() =>
                {
                    foreach(string fileName in fileNames)
                    {
                        file = new System.IO.StreamReader(fileName);
                        while ((producerLine = file.ReadLine()) != null)
                        {
                            foreach (BCtaskBC bc in bcs)
                            {
                                // string is reference type but it often acts like a value type
                                // may need to make a deep copy of producerLine for this next line
                                bc.BC.Add(producerLine);  // if  any queue size gets to 1000000 then this blocks
                            }
                        }
                        file.Close();
                    }                 
                    // Need to do this to keep foreach below from hanging
                    foreach (BCtaskBC bc in bcs)
                    {
                        bc.BC.CompleteAdding();
                    }
                });
    
                // Now consume the blocking collection with foreach. 
                // Use bc.GetConsumingEnumerable() instead of just bc because the 
                // former will block waiting for completion and the latter will 
                // simply take a snapshot of the current state of the underlying collection. 
                //  Method signature: Parallel.ForEach(IEnumerable<TSource> source, Action<TSource> body)
                Parallel.ForEach(bcs, bc =>
                {
                    foreach (string consumerLine in bc.BC.GetConsumingEnumerable())
                    {
                        bc.BCtask.ProcessTask(consumerLine);  
                    }
                } //close lambda expression
                     ); //close method invocation 
                // I think this need to be parallel
                //foreach (BCtaskBC bc in bcs)
                //{
                //    foreach (string consumerLine in bc.BC.GetConsumingEnumerable())
                //    {
                //        bc.BCtask.ProcessTask(consumerLine);
                //    }
                //}
            }
            public abstract class BCtaskBC
            {   // may need to do something to make this thread safe   
                private BlockingCollection<string> bc = new BlockingCollection<string>(1000000);  // this trotttles the size
                public BCtask BCtask { get; set; }
                public BlockingCollection<string> BC { get { return bc; } }
            }
            public abstract class BCtask
            {   // may need to do something to make this thread safe 
                public void ProcessTask(string S) {}
            }
        }
    }
    
    使用System.IO;
    使用System.Collections.Concurrent;
    使用系统线程;
    使用System.Threading.Tasks;
    命名空间阻止集合2
    {
    /// 
    ///MainWindow.xaml的交互逻辑
    /// 
    公共部分类主窗口:窗口
    {
    公共主窗口()
    {
    初始化组件();
    }
    public static void BC_getconsumineGenumerableCollection()
    {
    列表文件名=新建列表();//添加文件名
    细绳生产线;
    System.IO.StreamReader文件;
    List bcs=new List();//为每个消费者添加
    //开始制作任务
    Task.Factory.StartNew(()=>
    {
    foreach(文件名中的字符串文件名)
    {
    file=new System.IO.StreamReader(文件名);
    而((producerLine=file.ReadLine())!=null)
    {
    foreach(bcs中的BCtaskBC)
    {
    //字符串是引用类型,但它的行为通常类似于值类型
    //可能需要为下一行制作producerLine的深度副本
    Add(producerLine);//如果任何队列大小达到1000000,则该块
    }
    }
    file.Close();
    }                 
    //需要这样做才能防止下面的foreach被吊死
    foreach(bcs中的BCtaskBC)
    {
    bc.bc.CompleteAdding();
    }
    });
    //现在使用foreach使用阻塞集合。
    //使用bc.GetConsumingEnumerable()而不仅仅是bc,因为
    //前者将阻止等待完工,后者将阻止等待完工
    //只需拍摄基础集合当前状态的快照。
    //方法签名:Parallel.ForEach(IEnumerable源、动作体)
    Parallel.ForEach(bcs,bc=>
    {
    foreach(bc.bc.getconsumineGenumerable()中的字符串consumerLine)
    {
    bc.BCtask.ProcessTask(consumerLine);
    }
    }//关闭lambda表达式
    );//关闭方法调用
    //我认为这需要平行进行
    //foreach(bcs中的BCtaskBC)
    //{
    //foreach(bc.bc.getconsumineGenumerable()中的字符串consumerLine)
    //    {
    //bc.BCtask.ProcessTask(consumerLine);
    //    }
    //}
    }
    公共抽象类BCtaskBC
    {//可能需要执行一些操作以确保此线程安全
    private BlockingCollection bc=new BlockingCollection(1000000);//这将限制大小
    公共BCtask BCtask{get;set;}
    public BlockingCollection BC{get{return BC;}}
    }
    公共抽象类任务
    {//可能需要执行一些操作以确保此线程安全
    public void ProcessTask(字符串S){}
    }
    }
    }
    
    我最近解决了类似的问题。但我的解决方案不是用C#,而是用SQL,因为我有很高的耐用性要求。但也许我的一些想法会对你有所帮助(我会这样做):

    我使用了“工作单元”范式。在您的情况下,您可以选择一个工作单元,例如100-1000行文本。在您的情况下,每个工作单元的特征可能是文件名、开始文件位置和结束文件位置。每个单位也有一个标志,告诉它是否是进程