【问题标题】:How to use multiple async or buffered IO-Streams?如何使用多个异步或缓冲 IO-Stream?
【发布时间】:2018-06-22 06:51:15
【问题描述】:

我目前正在开发一个将大量数据 (~100mb) 写入 .txt 文件的程序。我在 Google 上搜索了一些关于如何优化不同 IO-Streams 的信息,但并没有真正理解以下内容:

  1. 如何一次打开多个流
  2. 如果流是默认缓冲的,或者我必须使用 BufferedStream
  3. 异步流和缓冲流之间的区别是什么

这是我输出的当前状态:

for (int Row = 0; Row < RowCount; Row++)
{                   
    using (System.IO.StreamWriter file =
        new System.IO.StreamWriter(@"C:\Calculations\data1.txt", true))
            {
                file.WriteLine(Time + "\t" + Row + "\t" + Value1[Row]);
            }
    using (System.IO.StreamWriter file =
        new System.IO.StreamWriter(@"C:\Calculations\data2.txt", true))
            {
                file.WriteLine(Time + "\t" + tRow + "\t" + Value2[Row]);
            }
}

我认为这是做什么的:

  • 打开数据流到 data1.txt
  • 写入 data1.txt
  • 关闭和刷新流
  • 打开数据流到 data2.txt
  • 写入 data2.txt
  • 关闭并刷新流

如果我的判断是正确的,那么对于 100mb 的数据来说,这将是大量的打开、关闭和刷新。我很想在开始时打开多个流,在计算时写入所有数据,然后关闭并刷新流。

【问题讨论】:

  • 操作系统已经异步写入文件,这是文件系统缓存的一项基本工作。写入内存,缓存管理器懒惰地将其写入磁盘。 100MB 不足以帮助它,实际上由于需要缓冲而变得更糟。
  • 为什么不把你的 using 语句移到 for 循环之前呢? using(StreamWriter file1=new StreamWriter(...), file2 = new StreamWriter(...)) { for(...) { file1.WriteLine(...); file2.WriteLine(...); }}

标签: c# iostream


【解决方案1】:

你的问题很多

一些没有特定顺序的随机点

  1. 所有流在 .net 中都有一个内部缓冲区
  2. 不,您不必缓冲它们,但是您可以出于性能原因调整内部缓冲区的大小(这取决于很多因素)
  3. 异步IO是一个很复杂的话题,从这里开始阅读Asynchronous File I/O

还有更多。

由于缓冲是由流处理的,所以您不必担心它,它会在需要时刷新它(根据默认值或您设置缓冲区的方式)。您无需关闭文件即可刷新。再一次,让流处理它,或者如果您愿意,您可以使用“Flush”自行刷新它

100mb 对于现代 SSD 驱动器来说并不算多,它可以在几毫秒内完成。再一次,如果您不需要,则无需打开和关闭它。然而,话虽如此,打开和关闭文件的开销却很小,因此有时将访问权限包装在 using 语句中并在和平用餐的基础上进行读写是有益/简单的

最后,是的,您可以打开多个流,但它们不是线程安全的。这意味着,由于它们只有 1 个内部缓冲区,因此您必须使用锁或打开和关闭文件以确保其完整性。这并不是说多个线程不能写入文件,只是说它不像它应该的那样微不足道。

如果您想保持文件打开以同步读取和写入,请不要忘记在完成后关闭/处置它们

祝你好运

【讨论】:

  • 如果我用不同的流写入不同的文件,每个文件都在自己的线程中,线程安全是否会成为问题?现在我打开和关闭我的流约 200.000 次,我希望每个文件都有一个流,我可以保持打开状态。
  • @LorenzMichalski 抱歉,如果不清楚,没有线程安全只是一个问题,如果你有线程从一个流写入和读取,你可以毫无问题地打开多个流,只要当您同步写入和读取它们时
【解决方案2】:

经过更多研究,我找到了第一个问题的解决方案。您可以像这样打开多个 IO-Stream:

StreamWriter file1 = File.CreateText(@"C:\Outputtests\OutputTest1.txt");
StreamWriter file2 = File.CreateText(@"C:\Outputtests\OutputTest2.txt");

像这样你需要自己刷新和关闭流。

我比较了运行时间,它为我的硬盘节省了大约 10000 倍:

static void Main(string[] args)
{
    Stopwatch sw1 = new Stopwatch();
    Stopwatch sw2 = new Stopwatch();
    Stopwatch sw3 = new Stopwatch();

    sw1.Start();                            //first version: opens and closes file each time -> takes around 1 minute total
    for (int i = 0; i < 10000; i++)
    {
        using (System.IO.StreamWriter file1 =
            new System.IO.StreamWriter(@"C:\Outputtests\OutputTest1.txt", true))
        {
            file1.WriteLine(i);
        }
    }
    sw1.Stop();

    sw2.Start();                            //second version: flushes each time -> takes around 50ms
    StreamWriter file2 = File.CreateText(@"C:\Outputtests\OutputTest2.txt");
    for (int i = 0; i < 10000; i++)
    {
        file2.WriteLine(i);
        file2.Flush();
    }
    file2.Close();
    sw2.Stop();

    sw3.Start();                            //third version: flushes at the end -> takes around 10ms
    StreamWriter file3 = File.CreateText(@"C:\Outputtests\OutputTest3.txt");
    for (int i = 0; i < 10000; i++)
    {
        file3.WriteLine(i);
    }
    file3.Flush();
    file3.Close();
    sw3.Stop();

    Console.WriteLine("Output 1:\t" + sw1.ElapsedMilliseconds.ToString() + Environment.NewLine + "Output 2:\t" + sw2.ElapsedMilliseconds.ToString() + Environment.NewLine + "Output 3:\t" + sw3.ElapsedMilliseconds.ToString());
    Console.ReadKey();
}

在必须刷新之前不确定应该在 IO-Stream 中存储多少。

【讨论】:

  • 它在 4k 左右刷新,这对于 ssd 来说效率极低。虽然它确实取决于你在做什么
猜你喜欢
  • 2012-03-11
  • 2014-10-25
  • 2011-06-08
  • 2011-02-09
  • 2014-09-12
  • 2010-11-29
  • 2015-05-17
  • 1970-01-01
相关资源
最近更新 更多