我想优化此代码:
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));
}
}
}
我处理大量数据并希望并行化流程。 通常数据存档,因此使用多个线程处理数据流非常重要
答案 0 :(得分:2)
如果你没有传递StreamReader
,而只是传递文件名,你可以写:
Parallel.Foreach(File.ReadLines(filename), (line) => action(sw, line));
如果您传递StreamReader
,仍然可以执行此操作。您只需创建一个将读取它的枚举器。像这里所做的一样:Recommended behaviour of GetEnumerator() when implementing IEnumerable<T> and IEnumerator<T>。使用它,你会写:
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]));
}
}
});
问题不在于锁定。问题是你持有锁太久了。编写它的方式,代码实际上是单线程的,因为所有线程都在等待该锁来进行处理。您需要更改处理,以便尽可能短的时间保持锁定。
将输出构建为StringBuilder
,将其转换为字符串,然后输出该字符串。例如:
string[] ls = line.Split('\t');
StringBuilder sb = new StringBuilder();
sb.Append(float.Parse(ls[0]));
sb.Append(' ');
sb.Append(int.Parse(ls[1])) * 10 + 1);
for (int i = 2; i < ls.Length; i++)
{
sb.Append(' ');
sb.Append(int.Parse(ls[i])); }
}
var sout = sb.ToString();
// lock and write
lock (sw)
{
sw.Write(sout);
}
你可以用StringWriter做同样的事情。
答案 1 :(得分:0)
最终解决方案:
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);
}
}
});
}
使用压缩输入流,我得到加速3次