内部方法"使用"语句会导致数据损坏或访问冲突的麻烦吗?

Jos*_*eph 4 c# multithreading using-statement

我有一个任务,将数据设置为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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

ang*_*son 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这有什么不同吗?好吧,不.在当前的实现中,处理内存流不会以任何方式将其丢弃.但是目前的实施或其未来没有任何保证,这是无证的行为.如果您希望此代码对于.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();
}
Run Code Online (Sandbox Code Playgroud)

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

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

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();
}
Run Code Online (Sandbox Code Playgroud)


Hen*_*man 5

调用ToArray();有效地复制了您想要的数据.
所以发生在MemStreams上的一切和任何事情都是无关紧要的.

更一般地,只要你的代码可以"看到"一片的数据,那么GC无法回收的那件.
不要过分思考这一点.

假设你曾经使用过:

 return m.GetBuffer();
Run Code Online (Sandbox Code Playgroud)

现在您将返回MemStream的内部缓冲区.m将被处置,但仅仅因为你返回它,缓冲区将比它的所有者寿命更长.