代码之家  ›  专栏  ›  技术社区  ›  Atif Aziz

是否保证GC.SuppressFinalize?

  •  6
  • Atif Aziz  · 技术社区  · 17 年前

    GC.SuppressFinalize 并不总是抑制对终结器的调用。可能是终结器被称为nontheless。因此我想知道 抑制定型 请求 而不是 根据系统?


    更多信息

    这个 抑制定型

    请求系统不调用 指定对象的终结器。

    我观察到了以下情况 SingletonScope Schnell 项目,基于 original idea by Ian Griffiths 只是更笼统些。其思想是在调试版本中检测 Dispose 处置 抑制定型 应该 防止终结器触发。不幸的是,这些警告似乎无论如何都会触发,但并不是以一种确定性的方式。也就是说,他们不会每次都开火。

    #region License, Terms and Author(s)
    //
    // Schnell - Wiki widgets
    // Copyright (c) 2007 Atif Aziz. All rights reserved.
    //
    //  Author(s):
    //      Atif Aziz, http://www.raboof.com
    //
    // This library is free software; you can redistribute it and/or modify it 
    // under the terms of the GNU Lesser General Public License as published by 
    // the Free Software Foundation; either version 2.1 of the License, or (at 
    // your option) any later version.
    //
    // This library is distributed in the hope that it will be useful, but WITHOUT
    // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
    // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
    // License for more details.
    //
    // You should have received a copy of the GNU Lesser General Public License
    // along with this library; if not, write to the Free Software Foundation, 
    // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
    //
    #endregion
    
    namespace WikiPad
    {
        #region Imports
    
        using System;
        using System.Diagnostics;
    
        #endregion
    
        //
        // NOTE: To use SingletonScope and ISingletonScopeHelper with value 
        // types, use Nullable<T>. For example, if the type of value to scope
        // is ThreadPriority then use ISingletonScopeHelper<ThreadPriority?>
        // and SingletonScope<ThreadPriority?>.
        //
    
        //
        // In debug builds, this type is defined as a class so a finalizer
        // can be used to detect an undisposed scope.
        //
    
        /// <summary>
        /// Designed to change a singleton and scope that change. After exiting
        /// the scope, the singleton is restored to its value prior to entering
        /// the scope.
        /// </summary>
    
        #if !DEBUG
        internal struct SingletonScope<T, H> 
        #else
        internal sealed class SingletonScope<T, H> 
        #endif
            : IDisposable 
            where H : ISingletonScopeHelper<T>, new()
        {
            private T _old;
    
            public SingletonScope(T temp)
            {
                _old = Helper.Install(temp);
            }
    
            private static H Helper
            {
                get { return new H(); }
            }
    
            public void Dispose()
            {
                //
                // First, transfer fields to stack then nuke the fields.
                //
    
                var old = _old;
                _old = default(T);
    
                //
                // Shazam! Restore the old value.
                //
    
                Helper.Restore(old);
    
                #if DEBUG
                GC.SuppressFinalize(this); // Only when defined as a class!
                #endif
            }
    
            #if DEBUG
    
            //
            // This finalizer is used to detect an undisposed scope. This will
            // only indicate that the scope was not disposed but (unfortunately)
            // not which one and where since GC will probably collect much later
            // than it should have been disposed.
            //
    
            ~SingletonScope()
            {
                Debug.Fail("Scope for " + typeof(T).FullName + " not disposed!");
            }
    
            #endif
        }
    }
    

    http://gist.github.com/102424 但请注意,到目前为止,还不能确定地重现该问题。

    5 回复  |  直到 15 年前
        1
  •  4
  •   Jon Skeet    17 年前

    一个奇怪的你 可以看到,即使实例方法仍在运行,终结器仍可以运行,只要该实例方法以后不使用任何变量。所以在您的示例代码中 Dispose 方法在第一行之后不使用任何实例变量。实例可以最终确定,即使 仍在运行。

    如果您插入对的呼叫 GC.KeepAlive(this) 处置 方法,你 发现问题消失了。

    克里斯·布鲁姆 blog post

        2
  •  3
  •   M. Jahedbozorgan    17 年前

    我总是使用这个设计模式来实现IDisposable接口。(这是微软建议的)而且对我来说GC.SuppressFinalize总是具有保证的性质!

    using System;
    using System.ComponentModel;
    
    //The following example demonstrates how to use the GC.SuppressFinalize method in a resource class to prevent the clean-up code for the object from being called twice.
    
    public class DisposeExample
    {
        // A class that implements IDisposable.
        // By implementing IDisposable, you are announcing that 
        // instances of this type allocate scarce resources.
        public class MyResource : IDisposable
        {
            // Pointer to an external unmanaged resource.
            private IntPtr handle;
            // Other managed resource this class uses.
            private readonly Component component = new Component();
            // Track whether Dispose has been called.
            private bool disposed;
    
            // The class constructor.
            public MyResource(IntPtr handle)
            {
                this.handle = handle;
            }
    
            // Implement IDisposable.
            // Do not make this method virtual.
            // A derived class should not be able to override this method.
            public void Dispose()
            {
                Dispose(true);
                // This object will be cleaned up by the Dispose method.
                // Therefore, you should call GC.SupressFinalize to
                // take this object off the finalization queue 
                // and prevent finalization code for this object
                // from executing a second time.
                GC.SuppressFinalize(this);
            }
    
            // Dispose(bool disposing) executes in two distinct scenarios.
            // If disposing equals true, the method has been called directly
            // or indirectly by a user's code. Managed and unmanaged resources
            // can be disposed.
            // If disposing equals false, the method has been called by the 
            // runtime from inside the finalizer and you should not reference 
            // other objects. Only unmanaged resources can be disposed.
            private void Dispose(bool disposing)
            {
                // Check to see if Dispose has already been called.
                if (!disposed)
                {
                    // If disposing equals true, dispose all managed 
                    // and unmanaged resources.
                    if (disposing)
                    {
                        // Dispose managed resources.
                        component.Dispose();
                    }
    
                    // Call the appropriate methods to clean up 
                    // unmanaged resources here.
                    // If disposing is false, 
                    // only the following code is executed.
                    CloseHandle(handle);
                    handle = IntPtr.Zero;
                }
                disposed = true;
            }
    
            // Use interop to call the method necessary  
            // to clean up the unmanaged resource.
            [System.Runtime.InteropServices.DllImport("Kernel32")]
            private extern static Boolean CloseHandle(IntPtr handle);
    
            // Use C# destructor syntax for finalization code.
            // This destructor will run only if the Dispose method 
            // does not get called.
            // It gives your base class the opportunity to finalize.
            // Do not provide destructors in types derived from this class.
            ~MyResource()
            {
                // Do not re-create Dispose clean-up code here.
                // Calling Dispose(false) is optimal in terms of
                // readability and maintainability.
                Dispose(false);
            }
        }
    
        public static void Main()
        {
            // Insert code here to create
            // and use a MyResource object.
        }
    }
    

    资料来源: MSDN: GC.SuppressFinalize Method

        3
  •  1
  •   codekaizen    16 年前

    我在终结器中抛出了一个invalidooperationexception,这样可以很容易地找到未正确处理的类型。在调用GC.SuppressFinalize的地方调用Dispose()时,我从未得到异常。

        4
  •  0
  •   Sam Saffron James Allen    17 年前

    请记住,调用GC.ReRegisterForFinalize将导致对象重新注册以进行finalize。

    例如在构造器中使用

    StackFrame frame = new StackFrame(1);
    

    并在决赛期间在调试消息中报告。

    另外,我注意到GC.supersfinalize不在finally子句中,如果在dispose期间抛出异常,则不会抑制对象finalizer。

        5
  •  0
  •   Sam Harwell    16 年前

    当构造具有用户定义终结器的对象时,运行时必须保留对该对象的内部引用,以便当在用户代码中无法访问该对象时,仍可以在运行时的终结线程中调用终结器。考虑到时间是调用终结器的关键,如果用户已请求禁止对象,则将其保留在队列中是没有意义的。在测试CLI实现中,我在具有用户定义的终结器的对象的头中保留了SuppressFinalizer标志。如果终结器线程到达队列中的对象时标志为true,则将跳过终结器调用。我不会从队列中移除对象,这样我就可以继续调用 GC.SuppressFinalize() O(1)代替O( N个 N个 是分配的可终结对象的数目(我可能稍后将此策略更改为延迟删除策略)。