4

我有一个任务,将数据设置到 FIFO 中,然后另一个线程一个一个地读取 FIFO 中的这些数据,稍后通过网络发送。调用时数据转换为字节数组FIFO.Add,如下:

public byte[] ByteArraySerialize()
{
    using (MemoryStream m = new MemoryStream())
    {
        using (BinaryWriter writer = new BinaryWriter(m))
        {
            writer.Write((int)this.Opcode);
            writer.Write(this.Data);
        }
        return m.ToArray();
    }
}

我的问题:在发送者线程从 FIFO 中读取数据之前,数据是否可能已损坏或处置?我的问题是了解using内部方法: 这是使用using内部方法的方式是否可能导致 GCMemoryStream在线程读取数据之前删除数据,让我们说在数据进入 FIFO 后几秒钟或几分钟后?

4

4 回答 4

6

有多种方法可以阅读这个问题,但让我们从显而易见的方式开始,它的编写方式:

这种在方法中使用“使用”的方式是否可能会导致 GC 删除内存流,在线程读取数据之前让我们说在数据进入 FIFO 后几秒钟或几分钟后?

不,这不会是一个问题。如果您能够在对 的调用中读取数据.ToArray(),那么您已经拥有数据的副本。如果 GC 稍后收集流,则该数组将继续存在。需要明确的是,关于 GC,如果您可以在调用处读取流内部的良好副本.ToArray(),那么该数组之后就可以了。根据文档,您获得的是内部数据的副本,而不是对其的引用,即使如此,如果您引用了某些内部数据结构,GC 将无法收集它。

但是,另一种解释可能是:这段代码有问题吗?

好吧,是的,也不是。

当前的实现BinaryWriter将在编写器实例被处置时处置底层流。这意味着MemoryStream将被处置。

让我复制您的代码并添加一些注释:

public byte[] ByteArraySerialize()
{
    using (MemoryStream m = new MemoryStream())
    {
        using (BinaryWriter writer = new BinaryWriter(m))
        {
            writer.Write((int)this.Opcode);
            writer.Write(this.Data);
        }

        // m is really disposed here
        return m.ToArray();
    }
}

这有什么不同吗?嗯,不。在当前的实现中,处理内存流不会以任何方式丢弃它。但是对于当前的实现或其未来没有任何保证,这是未记录的行为。如果您希望此代码对于 .NET 的未来版本或修补程序而言是稳定且值得信赖的,我不会这样写。

因此,我不会使用这种方式。我将重写代码如下:

using (MemoryStream m = new MemoryStream())
using (BinaryWriter writer = new BinaryWriter(m))
{
    writer.Write((int)this.Opcode);
    writer.Write(this.Data);

    writer.Flush();
    return m.ToArray();
}

这将要求编写器刷新所有数据,然后在释放该实例之前复制内存流的内部数组。

要么,要么使用重载的构造函数并要求编写者保持流打开:

using (MemoryStream m = new MemoryStream())
{
    using (BinaryWriter writer = new BinaryWriter(m, Encoding.UTF8, true))
    {
        writer.Write((int)this.Opcode);
        writer.Write(this.Data);
    }

    // m is no longer disposed here
    return m.ToArray();
}
于 2013-11-02T19:40:17.180 回答
5

调用ToArray();有效地复制了您想要的数据。
因此,发生在 MemStreams 上的一切都是无关紧要的。

更一般地说,只要您的代码可以“看到”一段数据,那么 GC 就无法回收该段数据。
不要想太多。

假设您使用过:

 return m.GetBuffer();

现在您正在返回 MemStream 的内部缓冲区。m将被 Disposed 但仅仅因为您返回了它,缓冲区将比它的所有者寿命更长。

于 2013-11-02T19:33:17.470 回答
1

我认为您的问题的答案是“在这种情况下不是”。内存流当然可以被处理,但在此之前,您将数据保存在 byte[] 数组中,该数组将保留。

尝试writer.Flush();writer.Write(this.Data);.

于 2013-11-02T19:35:47.330 回答
0

不,没有问题。流不会被过早处理。

您谈论的是 GC,但using语句的想法IDisposable是,当对象超出范围时,任何资源都会立即释放。我们不必等待 GC。换句话说,这与 GC 无关。

于 2013-11-02T19:31:33.737 回答