59

MSDN 网站指出:

缓冲区是内存中用于缓存数据的字节块,从而减少了对操作系统的调用次数。缓冲区提高了读写性能。缓冲区可以用于读取或写入,但不能同时用于两者。BufferedStream 的 Read 和 Write 方法会自动维护缓冲区。

我应该在每一个可能的场合使用这个类吗?

4

6 回答 6

73

根据布拉德艾布拉姆斯的说法,几乎从不:链接

不,将 BufferedStream 包装在 FileStream 周围的好处为零。大约 4 年前,我们将 BufferedStream 的缓冲逻辑复制到 FileStream 中,以鼓励更好的默认性能......事实上,我认为 .NET Framework 中没有任何 Streams 需要它,但自定义 Stream 实现可能需要它,如果他们默认不做缓冲。

于 2010-01-15T03:32:12.913 回答
21

以下是我正在学习的在线课程中的一些文字:

BufferedStream 类是一个扩展 Stream 类的具体类,用于为另一种类型的流(同步和异步)提供额外的内存缓冲区。创建类的实例时,必须将 BufferedStream 类配置为读取或写入,但不能将 BufferedStream 配置为同时执行这两项任务。

Microsoft 通过包含一个内置缓冲区提高了 .NET Framework 中所有流的性能。通过将 BufferedStream 应用于现有流(例如 FileStream 或 MemoryStream),性能显着提高。将 BufferedStream 应用到现有的 .NET Framework 流会产生双缓冲区。

BufferedStream 类最常见的应用是在不包含内置缓冲区的自定义流类中。

于 2009-01-29T16:22:18.960 回答
20

我知道的最好的情况是 BinaryFormatter 直接从 NetworkStream 序列化/反序列化。在中间使用 BufferedStream 可将性能提高十倍。

于 2011-09-05T07:11:17.963 回答
5

普通文件 I/O 流已经使用 StreamReader/StreamWriter 进行缓冲。

由于对流的读/写操作,通常使用带字节数组的读/写方法,你自然会自己提供一些缓冲。

如果您使用非常小的数组或使用 WriteByte,则可以通过在两者之间使用 BufferedStream 获得更好的性能。

于 2009-01-29T16:17:14.680 回答
4

迟到的答案,但无论如何都会回答。如果您了解它的工作原理,您就可以理解为什么在某些情况下使用它没有意义,以及为什么在其他情况下不使用它是有意义的。

为了证明这一点,我正在使用StreamWrapper类。只用这个类来看看你打断点的次数!在这个类中放置断点。我们的目标是查看调用 Write、Read 和其他方法的次数。

// This class is only used for demo purposes. Place a breakpoint on all parts
class StreamWrapper : Stream
{
    Stream stream;

    public StreamWrapper(Stream s)
    {
        stream = s;
    }

    public override bool CanRead => stream.CanRead;

    public override bool CanSeek => stream.CanSeek;

    public override bool CanWrite => stream.CanWrite;

    public override long Length => stream.Length;

    public override long Position { get => stream.Position; set => stream.Position = value; }

    public override void Flush()
    {
        stream.Flush();
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return stream.Read(buffer, offset, count);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return stream.Seek(offset,origin);
    }

    public override void SetLength(long value)
    {
        stream.SetLength(value);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        stream.Write(buffer, offset, count);
    }


}

现在您已经拥有了基本上是现有 strem 的包装器的包装器类,您可以进行以下测试:

示例写作:

// in real life you want this to be larger. 
int bufferSize = 8;


// Use BufferedStream to buffer writes to a MemoryStream.
using (var memory_test = new StreamWrapper(new MemoryStream()))
using (BufferedStream stream = new BufferedStream(memory_test, bufferSize))
{


    // all this will only send one write to memory_test!
    stream.Write(new byte[] { 1, 2 });
    stream.Write(new byte[] { 1, 2 });
    stream.Write(new byte[] { 1, 2 });
    stream.Write(new byte[] { 1, 2 });
    // BREAKPOINT ONLY HITS ONE TIME 


    // All this will also send only one write to memory_test
    for (int i = 0; i < 8; i++)                            
        stream.WriteByte(5);
    // BREAKPOINT ONLY HITS ONE TIME AGAIN INSTAD OF 8


    // this will send one write to memory_test. Writes of more than 8 bytes can happen!
    stream.Write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 });
    // ALL THIS WILL SEND ONE WRITE AGAIN
}

示例阅读:

// example reading
{
    // create stream with some data in it that we will be reading
    var ms = new MemoryStream();
    {
        // Write 256 bytes
        for (int i = 0; i <= byte.MaxValue; i++)
        {
            ms.WriteByte((byte)i);
        }
        ms.Position = 0;
    }

    // Use BufferedStream to buffer writes to a MemoryStream.
    using (var memory_test = new StreamWrapper(ms))
    {                                                        
        using (BufferedStream stream = new BufferedStream(memory_test, 8))
        {
            // note I do not care about the output of each read for demo breakpoint. On real life you will store that output
            // for now we only care how many times we hit the breakpoint because on real life that could be a slow/expensive 
            // operation such as opening a file for writing and you want to do those as few as possible.

            // hit breakpoint only one time with all this reads
            stream.ReadByte();
            stream.ReadByte();
            stream.ReadByte();
            stream.ReadByte();
            stream.ReadByte();
            stream.ReadByte();
            stream.ReadByte();
            stream.ReadByte();


            // hit breakpoint only one time with all this reads
            stream.Read(new byte[2] );
            stream.Read(new byte[2] );
            stream.Read(new byte[2] );
            stream.Read(new byte[2] );


            // hit breakpoint only one time even though it is larger than our buffer size 8
            // our goal is to hit the breakpoint as fewest time as possible because in real life
            // this could be slow/expensive operations
            stream.Read(new byte[1024] );

        }
    }
}

例如,如果这是对文件的写入操作,则可以通过确保始终一次写入至少 8 个字节而不是 1 个字节来提高性能。在现实生活中,您希望这个数字约为 4 KB

于 2020-03-25T23:53:13.957 回答
3

在每一个可能的场合都必须使用的是常识。在从 MemoryStream 读写时使用此类没有用处,但在进行网络或磁盘 IO 时它可能非常有用(如果这些子系统的 Streams 不自己进行缓冲)。

于 2009-01-29T16:17:08.190 回答