C# 适用于数百名消费者和大文件的好方法
我有几个包含数据的文件(每个文件将近1GB)。数据是一个字符串行 我需要与数百名消费者一起处理这些文件。这些消费者中的每一个都会进行一些不同于其他消费者的处理。消费者不会同时在任何地方写东西。它们只需要输入字符串。处理后,它们更新本地缓冲区。消费者可以轻松地并行执行 重要提示:对于一个特定的文件,每个使用者必须按照正确的顺序(如文件中所示)处理所有行(不跳过)。处理不同文件的顺序并不重要 一个消费者处理一条生产线的速度相对较快。我预计Corei5上的时间不到50微秒 所以现在我在寻找解决这个问题的好方法。 这将是.NET项目的一部分,所以请让我们只使用.NET(最好是C#) 我知道第三方物流和数据流。我想最相关的应该是C# 适用于数百名消费者和大文件的好方法,c#,.net,multithreading,task-parallel-library,tpl-dataflow,C#,.net,Multithreading,Task Parallel Library,Tpl Dataflow,我有几个包含数据的文件(每个文件将近1GB)。数据是一个字符串行 我需要与数百名消费者一起处理这些文件。这些消费者中的每一个都会进行一些不同于其他消费者的处理。消费者不会同时在任何地方写东西。它们只需要输入字符串。处理后,它们更新本地缓冲区。消费者可以轻松地并行执行 重要提示:对于一个特定的文件,每个使用者必须按照正确的顺序(如文件中所示)处理所有行(不跳过)。处理不同文件的顺序并不重要 一个消费者处理一条生产线的速度相对较快。我预计Corei5上的时间不到50微秒 所以现在我在寻找解决这个问题
BroadcastBlock
。但我认为这里的问题是,每一行我都必须等待所有消费者完成,才能发布新的一行。我想这不是很有效率
我认为理想情况是这样的:
我这样做对吗?无论是不是,我如何实现好的解决方案?我不同意一个线程从文件读取并写入缓冲区
在多个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行文本。在您的情况下,每个工作单元的特征可能是文件名、开始文件位置和结束文件位置。每个单位也有一个标志,告诉它是否是进程