当前位置: 首页 > 编程语言 > C#

我可以在终结器方法中使用哪些对象?Share

时间:2023-04-11 03:03:24 C#

我可以在终结器方法中使用哪些对象?我有一个类应该在处理或完成时删除一些文件。在终结器中,我不能使用其他对象,因为它们可能已经被垃圾回收了。我是否遗漏了有关终结器和字符串的一些要点?UPD:类似于:publicclassTempFileStream:FileStream{privatestring_filename;publicTempFileStream(stringfilename):base(filename,FileMode.Open,FileAccess.Read,FileShare.Read){_filename=文件名;}protectedoverridevoidDispose(booldisposing){base.Dispose(disposing);如果(_filename==null)返回;尝试{File.Delete(_filename);//<--糟糕!_filename可能已经被gc-ed_filename=null;}catch(Exceptione){...}}}是的,您绝对可以在终结器和许多其他对象类型中使用字符串。对于所有这些的权威来源,我会选择阅读C#,第3版,JeffreyRichter的CLR书。第21章对此进行了详细描述。无论如何,这就是真正发生的事情......在垃圾收集期间,任何具有仍要调用的终结器的对象都被放置在一个称为可释放列表的特殊列表中。这个列表被认为是根,就像静态变量和实时局部变量一样。因此,该对象本次引用的任何对象等都从垃圾回收周期中移除。它们将在当前的垃圾回收周期中存活下来,就好像它们没有资格开始回收一样。请注意,这包括字符串,这是您的问题,但它还涉及所有其他对象类型,然后从列表中删除对象。然后,下次垃圾回收运行的时候,又找到了同一个对象,但是这次finalizer不用再运行了,它已经执行过了,所以对象就正常回收了。在我告诉你什么不起作用之前,让我用一个例子来说明。假设你有对象A到Z,每个对象都引用下一个对象,所以你有对象A引用对象B,B引用C,C引用D,等等,直到Z。其中一些对象实现了终结器,它们都实现了IDisposable.假设A没有实现终结器而B实现了,然后其他一些人实现了,对于这个例子来说,除了A和B之外并不重要。你的程序保留对A的引用,并且只有A。在正常、正确的用法中模式,你会处理A,它会处理B,它会处理C,等等,但是你有一个错误,所以这不会发生。在某些时候,所有这些对象都符合收集条件。此时GC将找到所有这些对象,但后来注意到B有一个尚未运行的终结器。所以GC会把B放到freelist上,递归的把C、D、E等从GClist中拿掉给Z,因为B突然不合格了,其他的也一样。请注意,其中一些对象本身也放在空闲列表中,因为它们有自己的终结器,但它们引用的所有对象都将在GC中存活下来。但是,收集了A。让我澄清一下上一段。此时,A已经被收了,但是B、C、D等直到Z还活着,就好像什么事都没发生过一样。虽然您的代码不再引用它们中的任何一个,但一次性列表有。然后终结器线程运行并终结可释放列表中的所有对象,并从列表中删除这些对象。下次GC运行时,现在将收集这些对象。所以这绝对有效,那么什么是grandbruja?问题出在终结器线程上。线程不假设它应该最终确定这些对象的顺序。它不这样做是因为在许多情况下不可能这样做。正如我上面所说,在正常世界中,您会在A上调用dispose,它会处理B,它会处理C,等等。如果其中一个对象是流,则引用该流的对象在调用Dispose时可能会说“在处理流之前,我会继续刷新缓冲区。”这是完全合法的,很多现有代码都是这样做的。但是,在终结器线程中,不再使用此顺序,因此如果将流放在引用它的对象之前的列表中,则流将在引用它的对象之前完成,从而关闭流。换句话说,您不能做的事情总结如下:您不能访问您的对象引用的任何具有终结器的对象,因为您无法保证在终结器运行时这些对象将处于可用状态。对象将仍然存在于内存中而不被收集,但它们可能已经关闭、终止、终结等。所以,回到你的问题:问:我可以在终结器方法中使用字符串吗?回答:是的,因为字符串不实现终结器,并且不依赖于具有终结器的其他对象,所以在终结器运行时将是活跃的。使您走错路的假设是问题的第二句话:在终结器中,我不能使用其他对象,因为它们可能已被垃圾收集。正确的句子是:在终结器内部,我不能使用其他具有终结器的对象,因为它们可能已经被终结了。例如,终结器无法知道两个对象正确终结的顺序,请考虑两个相互引用的对象,并且两个对象都有终结器。终结器线程必须分析代码以确定它们通常被处理的顺序,这可能是两个对象之间的“舞蹈”。终结器线程不执行此操作,它只是一个接一个地完成,并且您无法保证哪个是第一个。那么,有没有什么时候可以安全地访问一个也有我自己的终结器的终结器的对象?唯一有保证的安全场景是你的程序/类库/源代码拥有这两个对象,所以你知道它是。在我解释之前,这不是好的编程习惯,因此您可能不应该这样做。示例:您有一个将数据写入文件的对象缓存,该文件永远不会保持打开状态,因此仅在对象需要向其写入数据时才打开。您有另一个使用第一个对象的CacheManager,并调用第一个对象为其提供要写入文件的数据。CacheManager有一个终结器。这里的语义是,如果一个管理器类被收集但没有被释放,它应该丢弃缓存,因为它不能保证它们的状态。但是,可以从缓存对象的属性中检索缓存对象的文件名。所以问题是,我是否需要将该文件名的副本复制到管理器对象中以避免在完成过程中出现问题?不,你不知道。当管理器完成时,缓存对象仍在内存中,它引用的文件名字符串也是如此。但是,您不能保证未运行缓存对象上的任何终结器。然而,在这种情况下,如果您知道缓存对象的终结器不存在,或者文件没有??被触及,管理器可以读取缓存对象的文件名属性,并删除该文件。但是,既然你现在有一个很奇怪的依赖,我肯定会反对。尚未提及的另一点是,虽然人们可能不希望对象的终结器在使用对象时运行,但终结机制并不能确保这一点。终结器可以在任意未知的线程上下文中运行;因此,他们应该避免使用任何非线程安全的类型,或者应该使用锁定或其他方法来确保他们只以线程安全的方式使用事物。请注意,终结器应使用Monitor.TryEnter而不是Monitor.Enter,并在意外持有锁时尽可能优雅地执行操作。请注意,由于终结器不应在对象仍在使用时运行,因此意外持有锁的事实通常表明终结器已过早运行。根据使用锁的代码的设计,可以让终结器设置一个标志并尝试再次获取锁,并在释放它之后,使用任何其他使用锁的代码来检查是否设置了该标志。如果是,重新注册对象以完成。在所有线程方案中正确处理终结清理很困难。终结可能看起来并不复杂,但没有方便的自动化机制来确保对象在使用相关对象时不会运行终结器。因此,终结器有许多微妙的线程安全问题。忽略此类问题的代码“通常”会工作,但有时会以难以诊断的方式失败。您可以在终结器中调用dispose方法,并在Dispose方法中使用文件清理代码。除此之外,您可以将一个布尔值传递给您的dispose方法,这表明您正在从终结器调用它。有关正确使用Dispose和Fianlizer的优秀参考,请阅读CorrectUseoftheIDisposableInterface以上是C#学习教程:WhatobjectscanIuseinafinalizermethod?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: