内部方法中的“using”语句会导致数据损坏或访问冲突的麻烦吗?

问题描述 投票:4回答: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的方式可能会导致GC删除MemoryStream,在线程读取数据之前让我们说在数据进入FIFO之后几秒或几分钟?

c# multithreading using-statement
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();
}

5
投票

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

更一般地说,只要您的代码可以“看到”一段数据,那么GC就无法回收该片段。 不要过分思考这一点。

假设你曾经使用过:

 return m.GetBuffer();

现在您将返回MemStream的内部缓冲区。 m将被处置,但仅仅因为你返回它,缓冲区将比它的主人更长。


1
投票

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

尝试在writer.Flush();之后添加writer.Write(this.Data);


0
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.