在我正在处理的 C++/CLI(和 C#)项目中,我遇到了似乎在早期调用终结器的问题。这似乎是一个非常复杂的问题,我将从代码中提到很多不同的类和类型。幸运的是,它是开源的,您可以在此处关注:Pstsdk.Net(mercurial 存储库) 我还尝试在适当的情况下直接链接到文件浏览器,这样您就可以在阅读时查看代码。我们处理的大部分代码都在pstsdk.mcpp
存储库的文件夹中。
现在的代码处于相当可怕的状态(我正在处理它),而我正在处理的代码的当前版本在Finalization fixes (UNSTABLE!)
分支中。该分支中有两个变更集,为了理解我冗长的问题,我们需要同时处理这两个。(变更集:ee6a002df36f和a12e9f5ea9fe)
对于某些背景,该项目是用 C++编写的非托管库的 C++/CLI 包装器。我不是该项目的协调员,我不同意几个设计决策,因为我相信很多看代码的人都会,但我离题了。我们将大部分原始库的层封装在 C++/CLI dll 中,但在 C# dll 中公开易于使用的 API。这样做是因为该项目的目的是将整个库转换为托管 C# 代码。
如果您能够获得要编译的代码,则可以使用此测试代码来重现问题。
问题
名为 的最新变更集moved resource management code to finalizers, to show bug
显示了我遇到的原始问题。此代码中的每个类都使用相同的模式来释放非托管资源。这是一个示例(C++/CLI):
DBContext::~DBContext()
{
this->!DBContext();
GC::SuppressFinalize(this);
}
DBContext::!DBContext()
{
if(_pst.get() != nullptr)
_pst.reset(); // _pst is a clr_scoped_ptr (managed type)
// that wraps a shared_ptr<T>.
}
这段代码有两个好处。首先,当这样的类在using
语句中时,资源会立即被适当地释放。其次,如果用户忘记了一个 dispose,当 GC 最终决定终结类时,非托管资源将被释放。
这是这种方法的问题,我根本无法理解,有时,GC 会决定最终确定一些用于枚举文件中数据的类。这发生在许多不同的 PST 文件中,我已经能够确定它与被调用的 Finalize 方法有关,即使该类仍在使用中。
我可以通过这个文件(下载)1始终如一地实现它。提前调用的终结器位于DBAccessor.cpp文件中的NodeIdCollection
类中。如果您能够运行上面链接的代码(由于对 boost 库的依赖,此项目可能难以设置),应用程序将失败并出现异常,因为列表设置为 null 并且指针为由于终结器运行而重置。_nodes
_db_
1) 类中的枚举代码是否有任何明显的问题NodeIdCollection
会导致 GC 在该类仍在使用时完成它?
我只能通过下面描述的解决方法使代码正常运行。
一个难看的解决方法
!classname
现在,我能够通过将所有资源管理代码从每个终结器 ( ) 移动到析构函数 ( )来解决这个问题~classname
。这已经解决了这个问题,尽管它并没有解决我对为什么课程提前完成的好奇。
但是,这种方法存在问题,我承认这更多的是设计问题。由于在代码中大量使用了指针,几乎每个类都处理自己的资源,并要求处理每个类。这使得使用枚举非常难看(C#):
foreach (var msg in pst.Messages)
{
// If this using statement were removed, we would have
// memory leaks
using (msg)
{
// code here
}
}
作用于集合中项目的 using 语句对我来说是错误的,但是,使用该方法非常有必要防止任何内存泄漏。没有它,即使调用了 pst 类的 dispose 方法,也永远不会调用 dispose 并且永远不会释放内存。
我有意改变这种设计。最初编写此代码时的基本问题,除了我对 C++/CLI 知之甚少甚至一无所知的事实之外,我无法将本机类放入托管类中。我觉得可以使用作用域指针,当类不再使用时会自动释放内存,但我不能确定这是否是一种有效的方法,或者它是否可以工作。所以,我的第二个问题是:
2) 以轻松的方式处理托管类中的非托管资源的最佳方法是什么?
clr_scoped_ptr
详细地说,我可以用最近添加到代码中的包装器替换本机指针吗(来自这个stackexchange问题的 clr_scoped_ptr.h )。或者我是否需要将本机指针包装在 a or之类的东西中?scoped_ptr<T>
smart_ptr<T>
感谢您阅读所有这些,我知道这很多。我希望我已经足够清楚,以便我可以从比我更有经验的人那里获得一些见解。这是一个很大的问题,我打算在允许的情况下增加赏金。希望有人可以提供帮助。
谢谢!
1此文件是可免费获得的 PST 文件安然数据集的一部分