具有IDisposable的自定义初始化函数

时间:2011-09-10 23:47:46

标签: c# .net garbage-collection idisposable

在.NET(C#)中,我遵循一些需要构造函数,初始化函数和IDisposable实现的自定义约定和模式。典型的课程如下图所示。不是直接在构造函数中完成初始化,而是通过专用函数来完成,该函数应该使对象可重用。但是,我不确定调用Dispose时会发生什么。如果GC调用它,则无论如何都会丢失对象的引用,因此不必担心。如果它是显式调用的,那么简单地调用Initialize并将类视为一个新对象是否有任何缺点,因为GC.SupressFinalize已被调用?大声笑,我相信我可以更容易地问这个问题。

public abstract class Thread: System.IDisposable
{

    protected bool Disposed { get; set; }
    protected bool Terminate { get; private set; }
    public bool IsRunning { get; private set; }
    private System.Threading.Thread ThreadObject { get; set; }

    public Thread ()
    {
        this.Initialize();
    }

    ~Thread ()
    {
        this.Dispose(false);
    }

    public virtual void Initialize ()
    {
        this.Stop();

        this.Disposed = false;
        this.Terminate = true;
        this.IsRunning = false;
        this.ThreadObject = null;
    }

    //====================================================================================================
    // Functions: Thread
    //====================================================================================================

    public void Start ()
    {
        if (!this.IsRunning)
        {
            this.IsRunning = true;
            this.Terminate = false;

            this.ThreadObject = new System.Threading.Thread(new System.Threading.ThreadStart(this.Process));
            this.ThreadObject.Start();
        }
    }

    /// <summary>
    /// Override this method to do thread processing.
    /// [this.Terminate] will be set to indicate that Stop has been called.
    /// </summary>
    /// <param name="template"></param>
    protected abstract void Process ();

    public void Stop (System.TimeSpan timeout)
    {
        if (this.IsRunning)
        {
            this.Terminate = true;

            try
            {
                if (timeout.TotalMilliseconds > 1D)
                {
                    this.ThreadObject.Join(timeout);
                }
                else
                {
                    this.ThreadObject.Join();
                }
            }
            catch
            {
                try
                {
                    this.ThreadObject.Abort();
                }
                catch
                {
                }
            }

            this.ThreadObject = null;
            this.IsRunning = false;
        }
    }

    //====================================================================================================
    // Interface Implementation: System.IDisposable
    //====================================================================================================

    public void Dispose ()
    {
        this.Dispose(true);

        System.GC.SuppressFinalize(this);
    }

    protected virtual void Dispose (bool disposing)
    {
        if (!this.Disposed)
        {
            if (disposing)
            {
                // Dispose managed resources.
                this.Stop(System.TimeSpan.FromSeconds(1));
            }

            // Dispose unmanaged resources here.

            // Note disposing has been done.
            this.Disposed = true;
        }
    }

}

4 个答案:

答案 0 :(得分:1)

GC永远不会调用Dispose,这取决于消费代码。然而,GC会调用终结器。这在最佳实践IDisposable实现中用于仅清理非托管代码。

如果在终结器的上下文之外使用Dispose,则GC不需要调用终结器,因此SuppressFinalize用作优化以防止它发生两次。

如果重复使用该对象,则会导致出现问题。从技术上讲,您可以在初始化时重新注册终结器,但这需要使线程安全。通常的做法是,对象在Disposed之后不会被重用,通常Dispose方法只能执行一次。 IMO初始化方法和对象重用为模式带来了复杂性,使其远离其预期目的。

答案 1 :(得分:1)

没有技术上的理由说明为什么你不能以这种方式重新激活一个被处置的物体,尽管我没有这样做,因为它违背了最少的意外原则(大多数一次性物体被使用过一次)。

如果你真的想这样做,我会避免使用终结器,这意味着你的IDisposable类不能直接拥有任何非托管资源。您可以通过将您的类在manged包装器中使用的任何非托管资源包装起来来实现此目的(例如,查看SafeHandle类)。

答案 2 :(得分:0)

我不喜欢你的线程处理的所有精确细节,但如果你要有一个每个实例拥有一个线程的类,你应该提供一个Dispose方法,它将确保实例的线程在一个线程中消失。有条不紊的时尚。

如果你想允许线程被清除,即使放弃了一个对象,你可能还需要创建一个包装器对象,外部应用程序保存一个引用但你的线程没有。该包装器对象的Finalize()方法应该以一种它会消失的方式轻推线程。该线程可以每隔几秒钟简单地轮询一个标志以查看它是否应该退出,或者可能存在更复杂的终止策略。

我很困惑,为什么Initialize调用Stop()?我原以为它会调用Start()。

答案 3 :(得分:0)

代码中使用了错误的语言模式应用示例。我清楚地看到了C#代码作者的C ++背景知识。不幸的是,C ++编码技术不适用于C#语言。

最好不要让对象进入垃圾收集器(GC),只需在其他地方引用它,就像在Singleton模式中那样,而不是尝试复活已处置的对象,或者使用Dispose模式,使用不允许完全控制的语言垃圾收集器和内存管理,例如,在C ++中就是如此。

简单地说,你不应该在C#中使用C ++习语,但提示和技巧是:

接口而不是C ++中的纯虚函数, 接口继承而不是C ++中的多类继承, 没有内存管理(使用弱引用)而不是C ++中完全受控对象的生命周期