我有线程1和线程2,我想在线程1中引发异常并在线程2中捕获它.
编辑
我现在可以看到我最初的解释令人困惑.我想要做的是从线程1初始化线程2中的异常引发.因此引发异常并在线程2中捕获,但此过程由线程1控制.
假设我有一个创建工作线程的主线程.我需要一种机制来优雅地从主线程中停止工作线程,但由于某些原因,这里无关紧要,我不能使用TThread.Terminate / Terminated模式.所以我认为,如果我可以启动(注入?)从主线程中提取工作线程,则可以将其用作停止信号.
解决方法
UKilThread单位
很好地打包用于提供AbortThread()例程的重用单元:
unit UKillThread; interface uses Classes,Windows,SysUtils; procedure AbortThread(const Th: TThread); implementation // Exception to be raized on thread abort. type EThreadAbort = class(EAbort); // Procedure to raize the exception. Needs to be a simple,parameterless procedure // to simplify pointing the thread to this routine. procedure RaizeThreadAbort; begin raise EThreadAbort.Create('Thread was aborted using AbortThread()'); end; procedure AbortThread(const Th: TThread); const AlignAt = SizeOf(DWORD); // Undocumented; Apparently the memory used for _CONTEXT needs to be aligned on DWORD boundary var Block:array[0..SizeOf(_CONTEXT)+512] of Byte; // The _CONTEXT structure is probably larger then what Delphi thinks it should be. Unless I provide enough padding space,GetThreadContext fails ThContext: PContext; begin SuspendThread(Th.Handle); ZeroMemory(@Block,SizeOf(Block)); ThContext := PContext(((Integer(@Block) + AlignAt - 1) div AlignAt) * AlignAt); ThContext.ContextFlags := CONTEXT_FULL; if not GetThreadContext(Th.Handle,ThContext^) then RaiseLastOSError; ThContext.Eip := Cardinal(@RaizeThreadAbort); // Change EIP so we can redirect the thread to our error-raizing routine SetThreadContext(Th.Handle,ThContext^); ResumeThread(Th.Handle); end; end.
演示项目
以下是使用AbortThread的方法:
program Project23; {$APPTYPE CONSOLE} uses SysUtils,Classes,UKillThread; var Th: TThread; type TTestThread = class(TThread) public procedure Execute;override; end; { TTestTrehad } procedure TTestThread.Execute; var N: Integer; begin try N := 1; while not Terminated do begin WriteLn(N); Inc(N); Sleep(1000); end; except on E:Exception do WriteLn(E.ClassName + ' / ' + E.Message); end; end; begin Th := TTestThread.Create(False); WriteLn('Press ENTER to raize exception in Thread'); ReadLn; AbortThread(Th); WriteLn('Press ENTER to exit'); ReadLn; end.
放弃
在确实使用它之前,请确保您了解此代码的作用.这绝不是正确的Terminate – Terminated逻辑(即协作线程关闭)的替代品,但它是TerminateThread()的更好替代品.这是在.NET Thread.Abort()方法之后建模的.我不知道实际的.NET方法是如何实现的,但是由于使用此代码的潜在问题是相似的,因此:
>该方法实际上并不终止该线程,它在线程的上下文中引发了一个EAbort派生的异常.线程的代码可能会捕获异常.这是不太可能的,因为不应该处理EAbort异常.
>该方法可能随时停止该线程.它可能在处理finally部分或设置新的异常帧时停止线程.即使您的线程使用正确的try-finally块,如果在分配资源之后但在将资源分配给变量之前引发异常,也可能导致内存或资源泄漏.
>如果线程在EnterCriticalSection之后立即中断并且恰好在通常遵循的try-finally之前,代码可能会导致死锁. MSDN page for EnterCriticalSection提到:“如果一个线程在拥有一个关键部分时终止,那么关键部分的状态是不确定的.”这对我来说是一个惊喜,我直觉地期望关键部分在拥有线程终止时被“释放”,但显然不是这样.