加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

delphi – 在另一个线程中引发异常

发布时间:2020-12-15 04:11:35 所属栏目:大数据 来源:网络整理
导读:如何在Delphi中的另一个线程中引发异常? 我有线程1和线程2,我想在线程1中引发异常并在线程2中捕获它. 编辑 我现在可以看到我最初的解释令人困惑.我想要做的是从线程1初始化线程2中的异常引发.因此引发异常并在线程2中捕获,但此过程由线程1控制. 假设我有一
如何在Delphi中的另一个线程中引发异常?
我有线程1和线程2,我想在线程1中引发异常并在线程2中捕获它.

编辑

我现在可以看到我最初的解释令人困惑.我想要做的是从线程1初始化线程2中的异常引发.因此引发异常并在线程2中捕获,但此过程由线程1控制.
假设我有一个创建工作线程的主线程.我需要一种机制来优雅地从主线程中停止工作线程,但由于某些原因,这里无关紧要,我不能使用TThread.Terminate / Terminated模式.所以我认为,如果我可以启动(注入?)从主线程中提取工作线程,则可以将其用作停止信号.

解决方法

这是一段将异常引发到另一个线程的示例代码.它使用SuspendThread来停止线程,GetThreadContext来读取线程的寄存器,改变EIP(指令指针),使用SetThreadContext然后使用ResumeThread来重启线程.有用!

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提到:“如果一个线程在拥有一个关键部分时终止,那么关键部分的状态是不确定的.”这对我来说是一个惊喜,我直觉地期望关键部分在拥有线程终止时被“释放”,但显然不是这样.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读