C# 多线程文件处理
我想优化此代码:C# 多线程文件处理,c#,multithreading,C#,Multithreading,我想优化此代码: public static void ProcessTo(this StreamReader sr, StreamWriter sw, Action<StreamWriter, string> action, FileProcessOptions fpo = null) { if (fpo == null) { fpo = new FileProcessOptions(); }
public static void ProcessTo(this StreamReader sr, StreamWriter sw, Action<StreamWriter, string> action, FileProcessOptions fpo = null)
{
if (fpo == null)
{
fpo = new FileProcessOptions();
}
List<string> buffer = new List<string>(fpo.BuferSize);
while (!sr.EndOfStream)
{
buffer.Clear();
while (!sr.EndOfStream && buffer.Count < fpo.BuferSize)
{
buffer.Add(sr.ReadLine());
}
if (fpo.UseThreads)
{
buffer.AsParallel().ForAll(line => action(sw, line));
}
else
{
buffer.ForEach(line => action(sw, line));
}
}
}
public static void ProcessTo(此StreamReader sr、StreamWriter sw、Action Action、FileProcessOptions fpo=null)
{
如果(fpo==null)
{
fpo=新文件处理选项();
}
列表缓冲区=新列表(fpo.BuferSize);
而(!sr.EndOfStream)
{
buffer.Clear();
而(!sr.EndOfStream&&buffer.Countaction(sw,line));
}
其他的
{
ForEach(line=>action(sw,line));
}
}
}
我处理大量数据,并希望并行处理。
通常数据是存档的,因此使用多个线程来处理数据流是非常重要的如果您不传递
StreamReader
,而只传递文件名,则可以编写:
Parallel.Foreach(File.ReadLines(filename), (line) => action(sw, line));
如果您传递一个StreamReader
,您仍然可以执行此操作。您只需要创建一个枚举器来读取它。类似于这里所做的事情:。用这个,你可以写:
LineReaderEnumerable myEnumerable = new LineEnumerator(sr);
Parallel.Foreach(myEnumerable, (line) => action(sw, line));
但是,您可能会遇到一个潜在的问题,因为您可能会有多个线程向该流编写器写入数据。而且StreamWriter
不支持并发写入。它将抛出一个异常。如果您正在同步对输出文件的访问(例如,使用锁),那么您在这里就可以了
您将遇到的另一个问题是输出的顺序。几乎可以肯定的是,如果您按照[1,2,3,4,…n]
的顺序读取行,输出顺序将不同。您可能会得到[1,2,4,3,6,5,7,9,8…n,n-1]
。如果输出顺序很重要,您必须想出一种方法来确保以正确的顺序输出
关于锁,您有:
sr.ProcessParalel(line =>
{
string[] ls = line.Split('\t');
lock (sw)
{
sw.Write(float.Parse(ls[0]));
sw.Write(int.Parse(ls[1]) * 10 + 1);
for (int i = 2; i < ls.Length; i++)
{
sw.Write(int.Parse(ls[1]));
}
}
});
您可以使用。最终解决方案执行大致相同的操作:
public static IEnumerable<string> GetEnumirator(this StreamReader sr)
{
while (!sr.EndOfStream)
{
yield return sr.ReadLine();
}
}
public static void ProcessParalel(this StreamReader sr, Action<string> action)
{
sr.GetEnumirator().AsParallel().ForAll(action);
}
public static void ProcessTo(this StreamReader sr, BinaryWriter bw, Action<BinaryWriter, string> action, FileProcessOptions fpo = null)
{
sr.ProcessParalel(line =>
{
using (MemoryStream ms = new MemoryStream())
{
BinaryWriter lbw = new BinaryWriter(ms);
action(lbw, line);
ms.Seek(0, SeekOrigin.Begin);
lock (bw)
{
ms.WriteTo(bw.BaseStream);
}
}
});
}
公共静态IEnumerable GetEnumirator(此StreamReader sr)
{
而(!sr.EndOfStream)
{
收益返回sr.ReadLine();
}
}
公共静态void ProcessParalel(此StreamReader sr,动作)
{
高级GetEnumirator().AsParallel().ForAll(操作);
}
公共静态void ProcessTo(此StreamReader sr、BinaryWriter bw、Action Action、FileProcessOptions fpo=null)
{
高级进程并行(行=>
{
使用(MemoryStream ms=new MemoryStream())
{
BinaryWriter lbw=新的BinaryWriter(毫秒);
动作(lbw,线路);
Seek女士(0,SeekOrigin.Begin);
锁(bw)
{
ms.WriteTo(bw.BaseStream);
}
}
});
}
在压缩输入流的情况下,我得到了3倍的加速度先生,您很可能会失望;通常,磁盘操作占用的时间占总时间的90%,而多线程处理对您没有帮助。你试过分析你的代码吗?探查器告诉了你什么?超过50%的CPU用于存档,我想在单独的线程中完成这项工作。你的操作只是写入其他文件吗?不,在数据处理方面几乎没有工作,只需要50%的CPU时间。请查看如何通知其他评论者你的答复。顺序不重要,但同步锁确实会变慢down@user2577265:是什么让你说同步锁减慢了速度?小例子:sr.ProcessParalel(line=>{string[]ls=line.Split('\t');lock(sw){sw.Write(float.Parse(ls[0]);sw.Write(int.Parse(ls[1])*10+1);for(int i=2;i
BinaryWriter
连接到a,输出到该文件,然后锁定该文件并将内存流的缓冲区复制到输出文件。除非您的个人记录是巨大的,否则您总是可以在锁之外的内存中构建它们,然后非常快速地获取锁并写入文件。关键是.NET文件I/O对象通常不允许多个并发操作,因此您必须同步访问。您实际上不需要自己的LineEnumerator
的任何精细实现。在YieldLines()方法中使用yield return
。
public static IEnumerable<string> GetEnumirator(this StreamReader sr)
{
while (!sr.EndOfStream)
{
yield return sr.ReadLine();
}
}
public static void ProcessParalel(this StreamReader sr, Action<string> action)
{
sr.GetEnumirator().AsParallel().ForAll(action);
}
public static void ProcessTo(this StreamReader sr, BinaryWriter bw, Action<BinaryWriter, string> action, FileProcessOptions fpo = null)
{
sr.ProcessParalel(line =>
{
using (MemoryStream ms = new MemoryStream())
{
BinaryWriter lbw = new BinaryWriter(ms);
action(lbw, line);
ms.Seek(0, SeekOrigin.Begin);
lock (bw)
{
ms.WriteTo(bw.BaseStream);
}
}
});
}