c# - 确保 "finally"在线程终止时在同一线程上运行

标签 c# finalize

这是给你们的一道谜题。我有一个多线程程序,其中一些线程使用托管资源,如锁和信号量。一些锁释放原语只能从完成锁获取的同一线程执行。

所以这是我的困惑:我包装了这些类型的操作:try { lock-acquire ... do something } finally { lock-release },但有时当我的线程终止时,finally 子句由 .NET 垃圾执行收集线程而不是我的线程。 (具体的case其实涉及到dispose of an object allocated in a using statement;详见下文)

这个演示有点棘手;我在 Isis2 中一直看到它,并且我已经通过检查获取和完成 block 中的线程 ID 发现这是发生的。但是我没有给你的 3 行演示,我很抱歉。我知道这样可以更轻松地提供帮助。

有没有一种方法可以延迟线程的终止,直到与该线程关联的所有挂起的 finalize block 都已执行?

---- 为 Mark 添加了详细信息 ----

我真正在做的是一个相当复杂的自检测锁定包,它具有各种锁定抽象(有界缓冲区、障碍、普通锁等)和线程优先级,旨在 self 检测死锁、优先级反转,非常慢锁授权和其他问题。我的代码足够复杂并且线程化程度很高,所以我需要它来调试它。

我的基本构造风格的一个例子是:

LockObject  myLock = new LockObject("AnIsis2Lock");

...

using(new LockAndElevate(myLock)) { stuff }

LockObject 是一个 self 监控的锁...LockAndElevate 会记录我锁定它(在本例中),然后在我解锁它时跟踪处置。因此,我正在利用这样一个事实,即 using 应该在 block 完成时处理新对象——即使它抛出异常也是如此。

我看到的是,线程经常终止,但 using dispose 事件实际上并没有发生;他们稍后在其他线程上运行。只有当我的一个线程终止时才会发生这种情况;在正常执行中,整个过程就像一个魅力。

因此,由于使用 translates to try... finally,我的问题是根据 finally 子句发布的。

最佳答案

到目前为止,这是我对自己问题的最佳答案,主要基于调试 Isis2 行为的经验。

如果线程不终止,“using(x = new something()) { }”(映射到“try { x= new something(); ...} finally { x.dispose }”)有效正如您所期望的那样:在退出代码块时发生处置。

但是异常扰乱了控制流。因此,如果您的线程抛出 IsisException,或者“某事”中的某事发生了抛出,控制权将传递给该异常的捕获。这是我正在处理的情况,我的 catch 处理程序在堆栈中更高。 C#/.NET 面临一个选择:是先捕获 IsisException,还是先处理?在这种情况下,我相当确定系统会首先系统地执行 IsisException。结果,分配对象“x”的终结器尚未执行但可运行,需要尽快调用。

[注意:对于那些好奇的人,Using 语句以调用 Dispose 结束,但根据文档,推荐的行为是使用终结器 ~something() { this.Dispose;只是为了覆盖所有可能的代码路径。然后可能会多次调用 Dispose,您应该保留一个标志,锁定并发性,并仅在第一次调用 Dispose 时释放您的托管资源。]

现在的关键问题是,在捕获到终止线程的异常的情况下,终结器显然可能不会在线程有机会终止之前运行;如果不是,C# 将通过在 GC 终结器线程上调用 dispose 来处理该对象。结果,如果像在我的代码中那样,x.Dispose() 解锁某些东西,则可能会发生错误:锁获取/释放必须发生在 .NET 上的同一线程中。所以这对你和我来说都是潜在错误的来源!在这种情况下,似乎在我的异常处理程序中调用 GC.WaitForFinalizers 会有所帮助。我不太清楚这是否能真正保证坏事不会发生。

我自己的代码中的另一个严重错误是我错误地在几个线程中捕获了 ThreadAbortException,这是由于对它们如何工作的一个古老的误解。不要这样做。我现在可以看出它会给 .NET 带来严重的问题。永远不要使用 Thread.Abort。

所以在这个理解的基础上,我修改了Isis2,现在它运行良好;当我的线程终止时,终结器似乎确实运行正常,dispose 似乎确实发生在线程退出之前(因此在它的 id 可以被重用之前,这让我感到困惑),一切都对世界有利。那些使用线程、优先级和自管理锁/屏障/有界缓冲区和信号量的人应该小心:这里潜伏着恶龙!

关于c# - 确保 "finally"在线程终止时在同一线程上运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11550638/

相关文章:

c# - 实例化 "AS"关键字

java - Java中的finalize()方法什么时候被调用?

Java 8 关于 finalize 方法的断言是否正确?

c# - vstest.console.exe 未发现 xunit 测试

c# - 错误 : Unable to find Mono. 确保 Mono 的 '/bin' 文件夹已添加到环境的 PATH 变量(在 MacOS 上)

c# - 为什么 Convert.ToDecimal(3.1922) 显示为 31922?

Kotlin super.finalize()

java - 发布类加载器时,什么时候对单例调用 finalize?

java - 多次调用finalize

c# - 数组的反序列化不支持 JSON 反序列化类型