【发布时间】:2019-11-10 06:42:03
【问题描述】:
我的理解是,C# 中存在终结器的主要原因是提供一个“安全网”,以防您有一个具有非托管资源的类而您忘记处置它。
让我们看看这个小 C# 方法:
public int GetValue()
{
var calculator = new Calculator();
return calculator.GetValue();
}
Calculator 类是一个包含非托管资源的 C++/CLI 类,应该清楚地处理它,最好是在 using 语句的帮助下。但是,嘿,我忘了这样做!
Calculator 类如下所示:
// header Calculator.h
public ref class Calculator
{
public:
Calculator();
~Calculator();
int GetValue();
protected:
!Calculator();
private:
CalculatorNative* _calculatorNative;
};
// implementation Calculator.cpp
Calculator::Calculator()
{
_calculatorNative = new CalculatorNative();
}
int Calculator::GetValue()
{
return _calculatorNative->GetValue();
}
Calculator::~Calculator()
{
this->!Calculator();
}
Calculator::!Calculator()
{
delete _calculatorNative;
_calculatorNative = nullptr;
}
CalculatorNative 类是一个标准的 C++ 类,它在其GetValue 方法中进行了一些耗时的计算(我认为这里的细节并不重要)。
现在,我忘记为其调用 Dispose 的 calculator 对象,在 CalculatorNative::GetValue() 被 Calculator::GetValue() 调用后,就可以进行垃圾回收了。
所以想象CalculatorNative::GetValue() 方法正处于其繁重计算的中间,现在 GC 开始四处嗅探并发现可以收集calculator(它的this 再也不会被使用)。但是它有一个终结器,因此它将终结器添加到终结器队列中。 CalculatorNative::GetValue() 方法仍在计算某些东西,现在 Calculator::!Calculator()(终结器)在不同的线程上运行,天啊,它删除了 _calculatorNative,即使它仍在做某事!当然,CalculatorNative::GetValue() 方法会严重崩溃。
现在的问题:
- 这真的是终结器的使用方式吗?
- 是否可以为我刚刚描述的如此简单的场景编写正确的终结器?
编辑: 现在我看到这个标题可能听起来有点冒犯。 我很抱歉。这绝对不是我的本意。 我只是花了太多时间调试这个问题:-(
【问题讨论】:
-
“C# 中存在的终结器是为了提供一个“安全网”,以防你有一个具有非托管资源的类而你忘记了释放它”——不,从不。事实上,有可能 GC 将根本不运行并且不会执行任何终结器。
-
如果没有对对象的引用,它怎么会在任何东西的中间呢?它是如何在没有完成它想要做的事情的情况下从调用中返回的?如果它确实产生了一个线程来做某事,那么这就是一个设计问题
-
这是一个很难掌握的话题,但处理这个问题的正确方法是确保在调用本机代码的过程中不会发生 GC。例如,你可以通过
int result = _calculatorNative.GetValue(); GC.KeepAlive(this); return result;来解决这个问题(我不确定你用什么语言写你的课,因为你使用的是->和::所以你必须适应你的语言) -
基本上,你是在引用
this来获得_calculatorNative,然后调用GetValue()方法,但如果这是对对象的最后一个 引用由this引用,现在可以根据优化规则进行收集。您需要确保您的this对象保持活动状态,直到您的本机代码返回。 -
所以,是的,它可以正常工作,但正如您所发现的那样,存在很多缺陷。
标签: c#