代码之家  ›  专栏  ›  技术社区  ›  avo

为什么WeakReference.IsAlive变为false?

  •  3
  • avo  · 技术社区  · 10 年前

    作为后续行动 this question ,我有以下代码:

    using System;
    using System.Runtime.InteropServices;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            class Child
            {
                public override string ToString()
                {
                    return "I am a child!";
                }
    
                ~Child()
                {
                    Console.WriteLine("~Child called");
                }
            }
    
            class Test
            {
                readonly object _child;
                readonly WeakReference _ref;
                readonly GCHandle _gch; // GCHandle is a value type, so it's safe
    
                public Test()
                {
                    _child = new Child();
                    _ref = new WeakReference(_child);
                    _gch = GCHandle.Alloc(_child);
                }
    
                // ...
    
                public void DoTest()
                {
                    lock (_child)
                    {
                        Console.WriteLine("DoTest called, child: " + _child.ToString() + ", is alive: " + _ref.IsAlive);
                    }
                }
    
                ~Test()
                {
                    Console.WriteLine("~Test starts");
                    DoTest();
                    _gch.Free();
                    Console.WriteLine("~Test ends");
                }
            }
    
            static void Main(string[] args)
            {
                var test = new Test();
                test.DoTest();
                test = null;
                GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
                System.Threading.Thread.Sleep(1000);
    
                GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
                Console.ReadLine();
            }
        }
    }
    

    输出:

    DoTest called, child: I am a child!, is alive: True
    ~Test starts
    DoTest called, child: I am a child!, is alive: False
    ~Test ends
    ~Child called
    

    问题是: 为什么 WeakReference.IsAlive 对于 _child 成为 false 在…内 ~Test() ,而 _儿童 对象仍被锁定 GCHandle.Alloc ?

    1 回复  |  直到 8 年前
        1
  •  2
  •   Erti-Chris Eelmaa    10 年前

    好吧,我记得从终结器访问“类实例变量”不是一个好主意,因为它们可能处于“随机”状态? 这基本上意味着WeakReference终结器将在类终结器之前调用。

    有一个专用于调用Finalize的特殊运行时线程 方法。当可交换队列为空时(通常是 case),此线程休眠。但当出现条目时,该线程会唤醒, 从队列中删除每个条目,并调用每个对象的Finalize 方法因此,不应在Finalize中执行任何代码 方法,该方法对执行 密码 例如,避免访问 定案方法。

    http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

    如果你确定了你的WeakReference,你可以得到更有意义的结果:

        public Test()
        {
            _child = new Child();
            _ref = new WeakReference(_child);
            _gch = GCHandle.Alloc(_child);
            _test = GCHandle.Alloc(_ref);
    
        }
    

    如果让GC知道WeakReference类,则可以得到相同的结果 本身 暂时无法收集,例如:

    static void Main(string[] args)
    {
        var test = new Test();
        var win = new WeakReference(test._child);
        test._ref = win;//new WeakReference(test._child);
    
        test.DoTest();
        test = null;
    }
    

    WeakReference的实际代码:

      ~WeakReference() {
                IntPtr old_handle = m_handle;
                if (old_handle != IntPtr.Zero) {
                    if (old_handle == Interlocked.CompareExchange(ref m_handle, IntPtr.Zero, old_handle))
                        GCHandle.InternalFree(old_handle);
                }
            }
    

    你可以看到,一旦它的终结器运行完毕,它就会释放句柄,并将其设置为零和零;IsAlive现在将报告错误。但引用本身实际上是活的。