C# 强制终结器命令

C# 强制终结器命令,c#,weak-references,finalizer,C#,Weak References,Finalizer,概述 我需要绑定一个具有4个主要功能的本机API: void ActivateEngine(); int CreateModule(); void DestroyModule(int id); void TerminateEngine(); 文档中指出,ActivateEngine和TerminateEngine应该包含对CreateModule和DestroyModule的任何调用。也就是说,用法应该类似于: void foo() { ActivateEngine(); i

概述

我需要绑定一个具有4个主要功能的本机API:

void ActivateEngine();
int CreateModule();
void DestroyModule(int id);
void TerminateEngine();
文档中指出,
ActivateEngine
TerminateEngine
应该包含对
CreateModule
DestroyModule
的任何调用。也就是说,用法应该类似于:

void foo()
{
    ActivateEngine();

    int module1 = CreateModule();
    int module2 = CreateModule();

    ...

    DestroyModule(module2);
    DestroyModule(module1);

    TerminateEngine();
}
为此,我创建了两个.NET对象,即
Engine
Module
,它们都使用
DllImport
属性绑定到本机API

引擎
对象作为单例,绑定到
激活引擎
终止引擎

模块
对象用于在
引擎
中创建多个实例,并绑定到本机API中的
CreateModule
DestroyModule

遇到问题

我的实现方式是,用户可以直接创建
模块
,而无需过多地了解
引擎
或对象的生命周期(即,我不[也不想]在不再使用时强制用户处理对象)

为此,我在
Engine
对象中使用了一个
WeakReference
列表,该列表指向所有创建的
模块

请参阅我的简化代码

问题是,当应用程序结束时,以非确定方式调用终结器,
WeakReference
目标已经为空,即使尚未调用
模块
的终结器,并且该参数
trackresurrenction
设置为true

在我的例子中,代码记录了以下内容:

ActivateEngine() ...
CreateModule() ==> 0 ...
CreateModule() ==> 1 ...
DestroyModule(1) ...
  Ooops ... Can't dispose registered module because WeakReference to it is already null ...
  Ooops ... Can't dispose registered module because WeakReference to it is already null ...
TerminateEngine() ...
DestroyModule(0) ...
这当然是不恰当的顺序

问题

如何在
引擎
之前强制完成所有
模块

我真的不想强迫最终用户在
模块
对象上调用
Dispose
方法,我也不想保留对已创建的
模块
的强引用,这样当对象不再在代码中引用时,对象可以自动消失。例如:

 processing
 {
     var module = new Module();
     ...
 }

 foo()
 {
     processing();
     GC.Collect(); // If using strong references 'module' is gonna be kept alive (that's not smart)  
 }
我使用
ConditionalWeakTable
查看了以下线程:


但我不明白这对我的情况有什么帮助。

与其说是一般问题的解决方案,不如说是针对您的特殊情况的解决方法:

在引擎单例和模块对象上分配终止引擎的义务

创建通过(或本机等效项)更新的共享计数器。这可能是一个
静态volatile int
字段或一段非托管内存

int
应该计算应用程序维护的引擎的“引用”数量。在每个构造函数中以原子方式递增计数器,在每个终结器中递减计数器。将计数器减少为零的一个终结器还调用
TerminateEngine()
(并释放共享计数器)


如果您的用户让所有
Module
对象被垃圾收集,但随后开始创建新模块,则引擎对象也必须算作“引用”。否则,发动机将提前损坏。

我认为你不能按你想要的方式来处理。最终确定的顺序是不确定的,因此不可能知道您的单例
引擎
模块
是否将首先确定

我建议您删除引擎单例(如果愿意,将引擎保持为静态类,但不允许任何实例,仅将其用于执行引擎初始化和终止的静态方法)和模块注册,并按照@Christian Klauser的回答使用静态原子计数器(在模块构造函数中递增,在终结器中递减)。当原子计数器从0变为1时,您可以激活引擎(通过调用内部静态引擎方法),并在模块计数变为0时终止引擎

我还建议您让用户在使用模块类时使用
使用
机制。

我最终使用了模式

这是通用解决方案,易于阅读/理解


请参阅更新的示例代码。

正如其他答案和评论所说,您需要实现某种形式的引用计数。下面是我的尝试(当您发布答案时,我正在进行此工作),它仍然使用单例
引擎(现在不需要这样做,您可以使它成为一个静态类,只需进行最小的更改),但是调用方需要调用
addreference()
releaserence()
,让引擎知道当计数分别达到1或0时是否需要设置或删除API

模块
支持在调用
Dispose()
或类完成时释放其引用

using System.Threading;

namespace FinalizerOrder
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;

    class Engine
    {
        private Engine()
        {
            //ActivateEngine() is no longer called here.
        }

        private readonly static Engine _singleton = new Engine(); //Now that the constructor is empty we can initialize immediately.
        private readonly static object _syncLock = new object();
        private static volatile int _counter = 0;

        public static Engine Singleton
        {
            get { return _singleton; }
        }

        public void AddRefrence()
        {
            lock (_syncLock)
            {
                _counter++;
                if (_counter < 0)
                    throw new InvalidOperationException("ReleaseRefrence() was called more times than AddRefrence()");

                if(_counter == 1)
                    Debug.WriteLine("ActivateEngine() ...");
            }
        }

        public void ReleaseRefrence()
        {
            lock (_syncLock)
            {
                _counter--;

                if (_counter < 0)
                    throw new InvalidOperationException("ReleaseRefrence() was called more times than AddRefrence()");

                if (_counter == 0)
                {
                    Debug.WriteLine("TerminateEngine() ...");
                }
            }
        }
    }

    class Module : IDisposable
    {
        public Module()
        {
            Engine.Singleton.AddRefrence();

            _id = _counter++;
            Debug.WriteLine("CreateModule() ==> {0} ...", _id);

        }

        private readonly int _id;
        private static int _counter;

        ~Module()
        {
            Dispose(false);   
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private bool _disposed = false;

        protected void Dispose(bool disposing)
        {
            if(_disposed)
                return;

            _disposed = true;                

            if (disposing)
            {
                //Nothing to do here, no IDisposeable objects.
            }

            Debug.WriteLine("DestroyModule({0}) ...", _id);
            Engine.Singleton.ReleaseRefrence();
        }
    }

    internal class Program
    {
        private static void Main()
        {
            Test();
            GC.Collect(3, GCCollectionMode.Forced);
            GC.WaitForPendingFinalizers();
            Test();

        }

        private static void Test()
        {
            var module1 = new Module();
            var module2 = new Module();

            GC.KeepAlive(module2);
            GC.KeepAlive(module1);
        }
    }
}
使用系统线程;
命名空间终结器顺序
{
使用制度;
使用System.Collections.Generic;
使用系统诊断;
类引擎
{
私人引擎()
{
//这里不再调用ActivateEngine()。
}
私有只读静态引擎_singleton=new Engine();//现在构造函数为空,我们可以立即初始化。
私有只读静态对象_syncLock=new object();
专用静态易失性int_计数器=0;
公共静态引擎单例
{
获取{return\u singleton;}
}
公共void addreference()
{
锁(同步锁)
{
_计数器++;
如果(_计数器<0)
抛出新的InvalidOperationException(“调用ReleaseRefrence()的次数比调用AddRefrence()的次数多”);
如果(_计数器==1)
WriteLine(“ActivateEngine()…”);
}
}
普布利