在C#中是否有一个很好的方法可以在给定的线程上抛出exception
我想写的代码是这样的:
void MethodOnThreadA() { for (;;) { // Do stuff if (ErrorConditionMet) ThrowOnThread(threadB, new MyException(...)); } } void MethodOnThreadB() { try { for (;;) { // Do stuff } } catch (MyException ex) { // Do the right thing for this exception. } }
我知道我可以让线程B以线程安全的方式定期检查线程A是否设置了一个标志,但这会使代码更复杂。 有没有更好的机制可供我使用?
这是一个更加充实的定期检查示例:
Dictionary exceptionDictionary = new Dictionary(); void ThrowOnThread(Thread thread, Exception ex) { // the exception passed in is going to be handed off to another thread, // so it needs to be thread safe. lock (exceptionDictionary) { exceptionDictionary[thread] = ex; } } void ExceptionCheck() { lock (exceptionDictionary) { Exception ex; if (exceptionDictionary.TryGetValue(Thread.CurrentThread, out ex)) throw ex; } } void MethodOnThreadA() { for (;;) { // Do stuff if (ErrorConditionMet) ThrowOnThread(threadB, new MyException(...)); } } void MethodOnThreadB() { try { for (;;) { // Do stuff ExceptionCheck(); } } catch (MyException ex) { // Do the right thing for this exception. } }
这不是一个好主意
本文讨论ruby的超时库。 它会跨线程抛出exception。
它解释了如何从根本上打破这样的事情。 它不仅仅是在ruby中被破坏,它在任何可以跨线程抛出exception的地方都会被破坏。
简而言之,可以(并且确实)发生的是:
ThreadA中:
At some random time, throw an exception on thread B:
ThreadB:
try { //do stuff } finally { CloseResourceOne(); // ThreadA's exception gets thrown NOW, in the middle // of our finally block and resource two NEVER gets closed. // Obviously this is BAD, and the only way to stop is to NOT throw // exceptions across threads CloseResourceTwo(); }
您的“定期检查”示例很好,因为您实际上并未在线程之间抛出exception。
你只是设置了一个标志,上面写着“下次你看这个标志时会抛出一个exception”,这很好,因为它不会受到“可以在你的捕获中间抛出或最终阻塞”的问题。
但是,如果您要这样做,您也可以设置一个“exitnow”标志,并使用它并节省创建exception对象的麻烦。 一个挥发性的bool可以正常工作。
有很多问题可以通过其他机制抛出线程,例如中止线程等,你应该找到另一种方法。
一个例外是一种机制,用于表示某个进程遇到了一些无法处理的特殊情况。 您应该尽量避免编写代码,以便使用exception来表示其他人遇到过exception情况。
其他线程很可能不知道如何在代码抛出exception的情况下处理exception。
简而言之,您应该找到一些其他机制来中止您的线程而不是使用exception。
使用事件对象或类似物告诉线程中止其处理,这是最好的方法。
在研究另一个问题时,我遇到了这篇文章,它让我想起了你的问题:
使用Rotor管道ThreadAbortException的深度
它显示了.NET实现Thread.Abort()的旋转 – 可能任何其他跨线程exception都必须类似。 (Yeech!)
猎户座爱德华兹所说的并不完全正确:不是“唯一”的方式。
// Obviously this is BAD, and the only way to stop is to NOT throw // exceptions across threads
在C#中使用CER( 约束执行区域 )允许您将资源作为primefaces操作发布,从而保护代码免受线程间exception的影响。 .NET Framework的几个类使用此技术,它与Windows的本机API一起使用,其中未发布的句柄可能导致内存泄漏。
请参阅http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimehelpers.prepareconstrainedregions.aspx
以下示例说明如何使用PrepareConstrainedRegions
方法可靠地设置句柄。 要可靠地设置指定预先存在的句柄的句柄,必须确保本机句柄的分配以及SafeHandle
对象中该句柄的后续记录是primefaces的。 这些操作之间的任何失败(例如线程中止或内存不足exception)都将导致本机句柄泄露。 您可以使用PrepareConstrainedRegions
方法确保句柄不会泄露。
很简单:
public MySafeHandle AllocateHandle() { // Allocate SafeHandle first to avoid failure later. MySafeHandle sh = new MySafeHandle(); RuntimeHelpers.PrepareConstrainedRegions(); try { } finally // this finally block is atomic an uninterruptible by inter-thread exceptions { MyStruct myStruct = new MyStruct(); NativeAllocateHandle(ref myStruct); sh.SetHandle(myStruct.m_outputHandle); } return sh; }
我很想知道你为什么要这样做。 没有一种简单的方法可以做到这一点,因为这不是一个好习惯。 您应该回到您的设计并找出一种更清洁的方法来实现最终目标。
我认为这不是一个好主意。对此问题采取另一种解决方法 – 尝试使用其他机制(如共享数据)在线程之间发出信号。
和其他人一样,我不确定这是一个好主意,但如果你真的想这样做,那么你可以创建一个SynchronizationContext的子类,允许发布委托给目标线程(如果它是一个WinForms线程的工作)为你完成,因为这样的子类已经存在)。 目标线程必须实现某种类型的消息泵,但要接收代理。
@Orion Edwards
我指出你在finally块中抛出exception的观点。
但是,我认为有一种方法 – 使用另一个线程 – 使用这种exception中断的想法。
线程A:
At some random time, throw an exception on thread C:
线程B:
try { Signal thread C that exceptions may be thrown //do stuff, without needing to check exit conditions Signal thread C that exceptions may no longer be thrown } catch { // exception/interrupt occurred handle... } finally { // ...and clean up CloseResourceOne(); CloseResourceTwo(); }
线程C:
while(thread-B-wants-exceptions) { try { Thread.Sleep(1) } catch { // exception was thrown... if Thread B still wants to handle exceptions throw-in-B } }
或者这只是愚蠢的?