编程语言
首页 > 编程语言> > c# – Finalizer在其对象仍在使用时启动

c# – Finalizer在其对象仍在使用时启动

作者:互联网

简介:C#/ .NET应该是垃圾回收. C#有一个析构函数,用于清理资源.当一个对象A被垃圾收集在同一行我试图克隆其变量成员之一时会发生什么?显然,在多处理器上,有时,垃圾收集器赢了……

问题

今天,在关于C#的培训课程中,老师向我们展示了一些仅在多处理器上运行时才包含错误的代码.

我将总结一下,有时候,编译器或JIT通过在从被调用方法返回之前调用C#类对象的终结器来搞砸.

Visual C 2005文档中给出的完整代码将作为“答案”发布,以避免提出非常大的问题,但必要的内容如下:

以下类具有“Hash”属性,该属性将返回内部数组的克隆副本.在构造中,数组的第一项值为2.在析构函数中,其值设置为零.

关键是:如果你试图得到“示例”的“哈希”属性,你将获得一个干净的数组副本,其第一个项目仍然是2,因为正在使用该对象(因此,不是垃圾收集/定稿):

public class Example
{
    private int nValue;
    public int N { get { return nValue; } }

    // The Hash property is slower because it clones an array. When
    // KeepAlive is not used, the finalizer sometimes runs before 
    // the Hash property value is read.

    private byte[] hashValue;
    public byte[] Hash { get { return (byte[])hashValue.Clone(); } }

    public Example()
    {
        nValue = 2;
        hashValue = new byte[20];
        hashValue[0] = 2;
    }

    ~Example()
    {
        nValue = 0;

        if (hashValue != null)
        {
            Array.Clear(hashValue, 0, hashValue.Length);
        }
    }
}

但没有什么是这么简单……
使用这个类的代码是在一个线程内部进行的,当然,对于测试,该应用程序是多线程的:

public static void Main(string[] args)
{
    Thread t = new Thread(new ThreadStart(ThreadProc));
    t.Start();
    t.Join();
}

private static void ThreadProc()
{
    // running is a boolean which is always true until
    // the user press ENTER
    while (running) DoWork();
}

DoWork静态方法是问题发生的代码:

private static void DoWork()
{
    Example ex = new Example();

    byte[] res = ex.Hash; // [1]

    // If the finalizer runs before the call to the Hash 
    // property completes, the hashValue array might be
    // cleared before the property value is read. The 
    // following test detects that.

    if (res[0] != 2)
    {
        // Oops... The finalizer of ex was launched before
        // the Hash method/property completed
    }
}

每隔1,000,000个DoWork提供一次,显然,垃圾收集器会发挥其魔力,并试图回收“ex”,因为它不再在函数的重新编写代码中引用,而这次,它比“哈希”更快得到方法.所以我们最终得到的是一个零ed字节数组的克隆,而不是正确的一个(第一项为2).

我的猜测是代码的内联,它基本上取代了DoWork函数中标记为[1]的行:

    // Supposed inlined processing
    byte[] res2 = ex.Hash2;
    // note that after this line, "ex" could be garbage collected,
    // but not res2
    byte[] res = (byte[])res2.Clone();

如果我们认为Hash2是一个简单的访问器,编码如下:

// Hash2 code:
public byte[] Hash2 { get { return (byte[])hashValue; } }

所以,问题是:这应该在C#/ .NET中以这种方式工作,还是可以认为这是JIT编译器的错误?

编辑

有关解释,请参阅Chris Brumme和Chris Lyons的博客.

http://blogs.msdn.com/cbrumme/archive/2003/04/19/51365.aspx
http://blogs.msdn.com/clyon/archive/2004/09/21/232445.aspx

每个人的回答都很有趣,但我不能选择一个比另一个好.所以我给了你一个1 …

抱歉

标签:c,net,garbage-collection,finalization
来源: https://codeday.me/bug/20190713/1448791.html