这是给你们的谜题。我有一个多线程程序,其中一些线程使用锁和信号量等托管资源。某些锁释放原语只能从执行锁获取的同一线程执行。
所以这是我的难题:我包装了这些类型的操作:尝试{lock-quire...做某事}最终{锁-释放},但有时当我的线程终止时,最终子句是由.NET垃圾收集线程执行的,而不是由我的线程执行的。(具体情况实际上涉及到在"using“语句中分配的对象的Dispose;详情见下文)
这在演示中有点棘手;我在Isis2中经常看到它,并且我通过检查acquire和finalize块中的线程it发现了这一点。但是我没有一个3行的演示给你,我对此感到抱歉。我知道这会让你更容易提供帮助。
有没有一种方法可以延迟线程的终止,直到与该线程关联的所有挂起的finalize块都执行完毕?
-为标记添加的详细信息
我真正在做的是一个相当复杂的自检锁包,它有各种具有线程优先级的锁抽象(有界缓冲区、障碍、普通锁等),并设计用于自检测死锁、优先级反转、非常慢的锁授予和其他问题。我的代码足够复杂,线程化程度也足够高,所以我需要用它来调试它。
我的基本构造风格的一个例子是:
LockObject myLock = new LockObject("AnIsis2Lock");
...
using(new LockAndElevate(myLock)) { stuff }LockObject是一种自监控锁...LockAndElevate会记录我锁定了它(在本例中),然后在我解锁它时跟踪dispose。因此,我利用了这样一个事实,即使用应该在块完成时处理新对象--即使它抛出异常。
我所看到的是,线程经常会终止,但using dispose事件并未实际发生;它们稍后会在其他线程上运行。只有当我的一个线程终止时,才会发生这种情况;在正常执行中,整个过程就像符咒一样工作。
因此,既然使用翻译来尝试...最后,我的问题被贴在了finally子句中。
发布于 2012-07-21 19:38:49
因此,到目前为止,这是我对我自己问题的最好回答,主要是基于调试Isis2行为的经验。
如果线程没有终止,"using( x = new using()){ }“(映射为"try {x= new something();...} finally { x.dispose }")的工作方式与您预期的完全一样: dispose在代码块退出时发生。
但是异常会中断控制流。因此,如果你的线程抛出了一个IsisException,或者" something“中的某个东西抛出了这个异常,那么控制就会传递给该异常的catch。这就是我正在处理的情况,而我的catch处理程序在堆栈中的位置更高。C#/.NET面临一个选择:是先捕获IsisException,还是先进行dispose?在这种情况下,我相当确定系统首先系统地执行IsisException。因此,已分配对象"x“的终结器尚未执行,但可以运行,需要立即调用。
注意:对于那些好奇的人,Using语句以调用Dispose结束,但是根据文档,推荐的行为是有一个终结器~something() { this.Dispose;},只是为了覆盖所有可能的代码路径。然后,Dispose可能会被多次调用,您应该保留一个标记,锁定以防止并发,并仅在第一次调用Dispose时释放托管资源。
现在的关键问题是,在捕获的异常终止线程的情况下,终结器显然可能不会在线程有机会终止之前运行;如果不是这样,C#将通过在GC终结器线程上调用dispose来处理对象。因此,如果像我的代码一样,x.Dispose()解锁了一些东西,那么就会发生错误:锁获取/释放必须发生在.NET上的同一线程中,这对你和我来说都是潜在错误的来源!在这种情况下,在异常处理程序中调用GC.WaitForFinalizers似乎会有所帮助。对我来说,不太清楚的是,这是否是不会发生坏事的真正保证。
在我自己的代码中还有一个严重的错误,那就是我错误地在几个线程中捕获了ThreadAbortException,这是因为我对这些线程的工作原理有一个古老的误解。不要这样做。我现在可以看到它给.NET带来了严重的问题,只是永远不要使用Thread.Abort。
因此,基于这个理解,我已经修改了Isis2,现在它工作得很好;当我的线程终止终结器时,看起来确实可以正确运行,dispose似乎确实在线程退出之前发生(因此在它的id可以重用之前,这让我感到困惑),一切都是好的。那些使用线程、优先级和自管理锁/屏障/有界缓冲区和信号量的人应该小心:这里潜伏着龙!
https://stackoverflow.com/questions/11550638
复制相似问题