静态析构函数

4
我在一个Web服务中实例化了一个类,其中静态成员保存了一些资源。如果我不是静态地持有这些资源,我可能会通过某个IDisposable对象访问它们,在Dispose时释放资源。无论是否保留此会话是一个好主意,.NET是否提供任何方法在类型被静态析构时调用任何清理代码?
请不要回答任何类似于“停止在静态成员变量中保存资源”的问题。我理解静态保留此信息的缺点,并愿意接受后果(我们正在使用它来将处理时间从58小时减少到4小时,用于某些批处理)。该问题具体是:在这种情况下,是否有办法可以很好地清除这些资源?
编辑: 我知道这个类将在进程的剩余生命周期内存在,但是使用静态构造函数,.NET提供了在将该类型加载到内存中时执行某些操作的机会。是否可以在相反的端口上执行任何操作?

10
何时会对静态类型进行析构? - haiyyu
@haiyyu - 这是一个非常好的问题,我不知道。 - LJM
3
听起来你并没有真正理解使用静态变量的缺点,其中一个缺点是“它们的生命周期就是AppDomain的生命周期”......我建议你调查一下能否将其与Web服务的生命周期联系起来,仍然避免使用静态成员。 - Jon Skeet
@JonSkeet - 我知道静态意味着它们的生命周期与应用程序域相同。但是,当您将类型加载到内存中时,您会获得静态构造函数的功能。当AppDomain死亡时,一切是否都消失了,无法运行任何清理代码?我觉得我的困惑在于AppDomain死亡时会发生什么。 - LJM
@LJM:你可以挂钩 AppDomain.DomainUnload,但我强烈建议不要这样做。 - Jon Skeet
不过,开玩笑的,如果Jon Skeet说不要这样做,我也不会这样做。我只是好奇它将如何发生。谢谢。 - LJM
5个回答

4
实际上,无法通过托管代码进行操作。您想要处理的是卸载程序集,但在大多数情况下,当您希望它发生时,不会发生这种情况。
更详细地说:
有一个AppDomain.DomainUnload事件(http://msdn.microsoft.com/en-us/library/system.appdomain.domainunload.aspx),您可以进行处理。当应用程序域从其托管进程(例如ASP.NET)卸载时,会处理此事件。
但是,如果您是EXE,或者托管EXE正在被回收,则不会引发此事件。如果正确设置,您可能能够处理本机DLL_PROCESS_DETACH并将其反弹回托管代码,但由于加载器锁定,您必须非常小心从该上下文中执行的任何操作(触发程序集加载的任何内容都会死锁)。
您可以阅读此内容以了解所需的清理工作(提示:不需要太多):http://blogs.msdn.com/b/oldnewthing/archive/2012/01/05/10253268.aspx

基本上,你唯一需要担心的就是将缓冲区刷新到磁盘上,如果你需要做更复杂的操作,那么你已经搞砸了。malloc(),因此new()可能会立即使你的程序崩溃。这同样适用于托管代码。


3

这个问题并没有太多意义,静态资源的生命周期与进程相同,当一个进程结束时,操作系统会清理所有资源。如果进程已经停止运行,那么它就不能继续使用资源。


我试图过于概括这个问题。在这种特定情况下,我们已经打开了一个与其他地方的一些服务相关的会话。能够关闭该会话将是很好的。 - LJM
2
我认为您需要提出一个更具体的问题,说明您应用程序的详细信息。例如,ASP.NET具有一个名为application_end的方法,它允许您在进程结束或重新启动时执行此类操作。但对于Windows服务,答案将是不同的。 - Ben Robinson

2
什么时候这个静态状态变得不重要?此时,您应该将其销毁。
销毁可能意味着“释放一些未管理的内存,将缓存写入数据库并将静态变量设置为null”。
最后访问点在不同的应用程序中会有不同的含义。在ASP.NET应用程序中,您无法可靠地确定此点。它在Application_End事件或AppDomain.Unload事件触发时到来,以先到者为准。WCF也是如此。在WinForms应用程序中,您需要在主窗体关闭后或主应用程序的最后一行执行此操作。
无论哪种情况,您都需要自己清理。
替代方案:您可以将状态封装到可终结对象中。它将在AppDomain卸载时清除。如果编写所谓的关键终结器,您几乎可以保证您的清理将执行。

我刚刚添加了一个实用的替代方案。 - usr

1

你不能销毁一个未被实例化的对象。

我认为你应该使用单例模式,而不是静态地持有所有数据。


按照这个逻辑,你可以说你不能“构造”一个静态的东西,但这是不正确的。我们有静态构造函数。 - LJM
假设我们有一个名为Foo的类,其中包含一个静态构造函数。当它的静态构造函数被调用时,我们在该构造函数中没有实例化任何Foo对象。静态构造函数仅初始化该类,而不是该类的对象。 - Peter Kiss

-1
如果您存储为静态成员的对象正确实现了IDisposable接口,那么.NET运行时会在应用程序卸载时处理任何资源。如果其中任何一个对象没有这样做,那么建议您创建实现IDisposable接口的包装类,以便您可以自己清理资源。 MSDN上的IDisposable

你有什么可以让我参考以确认这个吗?我想更好地理解它是如何工作的。 - LJM
除了 msdn 中的 IDisposable 文档(其中已经很好地解释了),我不知道有任何涉及您确切情况的参考资料。如果您创建一个包装类,您可以附加调试器,然后卸载进程并查看发生了什么。您应该能够在 Dispose 方法中设置断点,或者写入日志或其他内容以证明它被调用了。 - Ray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接