Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading Delphi线程异常机制_Multithreading_Delphi_Exception Handling - Fatal编程技术网

Multithreading Delphi线程异常机制

Multithreading Delphi线程异常机制,multithreading,delphi,exception-handling,Multithreading,Delphi,Exception Handling,在delphi中线程如何工作,以及为什么在线程应该引发异常的时候,异常却没有显示出来,这让我左右为难。下面是带注释的代码,也许有人可以向我解释一下线程或delphi是如何管理访问冲突的 //线程代码 unit Unit2; interface uses Classes, Dialogs, SysUtils, StdCtrls; type TTest = class(TThread) private protected j: Integer; pr

在delphi中线程如何工作,以及为什么在线程应该引发异常的时候,异常却没有显示出来,这让我左右为难。下面是带注释的代码,也许有人可以向我解释一下线程或delphi是如何管理访问冲突的

//线程代码

unit Unit2;

interface

uses
  Classes,
  Dialogs,
  SysUtils,
  StdCtrls;

type
  TTest = class(TThread)
  private
  protected
    j: Integer;
    procedure Execute; override;
    procedure setNr;
  public
    aBtn: tbutton;
  end;

implementation


{ TTest }

procedure TTest.Execute;
var
  i                 : Integer;
  a                 : TStringList;
begin
 // make severals operations only for having something to do
  j := 0;
  for i := 0 to 100000000 do
    j := j + 1;
  for i := 0 to 100000000 do
    j := j + 1;
  for i := 0 to 100000000 do
    j := j + 1;
  for i := 0 to 100000000 do
    j := j + 1;
  for i := 0 to 100000000 do
    j := j + 1;
  for i := 0 to 100000000 do
    j := j + 1;
  for i := 0 to 100000000 do
    j := j + 1;
  for i := 0 to 100000000 do
    j := j + 1;

  Synchronize(setnr);
  a[2] := 'dbwdbkbckbk'; //this should raise an AV!!!!!!

end;

procedure TTest.setNr;
begin
  aBtn.Caption := IntToStr(j)
end;

end.
项目代码

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,
  Unit2, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
  public
    nrthd:Integer;
    acrit:TRTLCriticalSection;
    procedure bla();
    procedure bla1();
    function bla2():boolean;
    procedure onterm(Sender:TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.bla;
begin
 try
  bla1;
 except on e:Exception do
   ShowMessage('bla '+e.Message);
 end;
end;

procedure TForm1.bla1;
begin
 try
  bla2
 except on e:Exception do
   ShowMessage('bla1 '+e.Message);
 end;
end;

function TForm1.bla2: boolean;
var ath:TTest;
begin
 try
  ath:=TTest.Create(true);
   InterlockedIncrement(nrthd);
  ath.FreeOnTerminate:=True;
  ath.aBtn:=Button1;
  ath.OnTerminate:=onterm; 
   ath.Resume;
 except on e:Exception do
  ShowMessage('bla2 '+e.Message);
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);

begin
//
 try
   bla;
   while nrthd>0 do
    Application.ProcessMessages;
 except on e:Exception do
  ShowMessage('Button1Click '+e.Message);
 end;
 ShowMessage('done with this');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 nrthd:=0;
end;

procedure TForm1.onterm(Sender: TObject);
begin
 InterlockedDecrement(nrthd)
end;

end.
此应用程序的目的只是了解访问冲突被捕获的位置,以及应如何编写代码。

我不明白为什么在“a[2]:='dbwdbkbckbk';”行中没有引发AV

线程是一个你应该接受异常的地方。

在线程中处理异常的要点是,如果希望将异常显示给最终用户,则应捕获异常并将其传递给主线程,以便安全地显示异常

您将在这个EDN线程中找到一些示例

程序TMyThread.DoHandleException;
开始
//取消鼠标捕获
如果GetCapture为0,则发送消息(GetCapture,WM_CANCELMODE,0,0);
//现在实际显示异常
如果feexception是Exception,那么
Application.ShowException(feException)
其他的
SysUtils.ShowException(feException,nil);
结束;
程序TMyThread.Execute;
开始
FEException:=零;
尝试
//提出例外
创建('我引发了一个异常');
除了
手部异常;
结束;
结束;
程序TMyThread.HandleException;
开始
//此函数是虚拟的,因此您可以覆盖它
//并添加您自己的功能。
FEException:=异常(ExceptObject);
尝试
//不显示EAPort消息
如果不是(feexception是EAbort),则
同步(DoHandleException);
最后
FEException:=零;
结束;
结束;

在Delphi 2005中-可能还有大多数其他版本-如果异常未经处理就从
Execute
方法中逃逸,那么它将被调用
Execute
的函数捕获并存储在线程的
FatalException
属性中。(查看Classes.pas,
ThreadProc
)在释放线程之前,不会对该异常执行任何进一步的操作,此时该异常也会被释放

因此,您有责任检查该财产并对其采取措施。您可以在线程的
OnTerminate
处理程序中检查它。如果它不为null,那么线程将由于未捕获的异常而终止。例如:

procedure TForm1.onterm(Sender: TObject);
var
  ex: TObject;
begin
  Assert(Sender is TThread);
  ex := TThread(Sender).FatalException;
  if Assigned(ex) then begin
    // Thread terminated due to an exception
    if ex is Exception then
      Application.ShowException(Exception(ex))
    else
      ShowMessage(ex.ClassName);
  end else begin
    // Thread terminated cleanly
  end;
  Dec(nrthd);
end;

没有必要使用互锁函数来跟踪线程数。线程创建函数和终止处理程序始终在主线程的上下文中运行。普通旧的
Inc
Dec
就足够了。

我们也可以重新提高FatalException。重新启动似乎不符合逻辑,但如果您的代码中有一个中央异常/错误处理程序,并且您只想将线程异常包括在该机制中,则可以在一些罕见的情况下重新启动:

procedure TForm1.onterm(Sender: TObject);
var
  ex: Exception;
begin
  Assert(Sender is TThread);
  ex := Exception(TThread(Sender).FatalException);
  if Assigned(ex) then
    // Thread terminated due to an exception
    raise ex;
  Dec(nrthd);
end;
“a”变量未初始化!它可以指向计算机中任何可能的内存位置。它甚至可以指向物理上不存在的位置(尽管由于虚拟内存系统,这是没有意义的)

因此,在您的程序中,如果“a”意外地指向一个有效的内存地址(我指的是进程拥有的地址),那么您的代码将在该位置写入,而不会发生访问冲突。我认为你至少应该把a改为零


请参见Remy Lebeau的评论:

这也是:

调试器没有告诉您异常情况吗?a变量没有初始化!如果它指向一个有效的内存地址怎么办?我指的是流程拥有的地址。然后,您的代码将在该位置写入,而不会发生访问冲突。我说得对吗?我认为你至少应该把a改为零。请看Remy Lebeau的评论。它解释了什么是AV以及它是如何发生的:+1。我从来没见过肥胖感?。。。哦,等等,我们还在使用Delphi5。我再也没有该版本的源代码了,@Lieven。如果它有
AcquireExceptionObject
,那么您可以自己模拟新的
FatalException
行为。@Lieven:在D5和D6中,我认为线程的执行方法还没有受到保护。。。您必须在Execute的重写中自己执行。D6中引入了
TThread.FatalException
属性(以及
System.AcquireExceptionObject()
函数)。在D5中,
Execute()
仅由
try..finally
包装,以确保始终调用
DoTerminate()
,但在将引发的异常发送到
finally
之外的处理程序之前,线程被终止(通过
EndThread()
)。在D6中,
TThread.Execute()
由一个
try..包装,除了在调用
DoTerminate()
之前获取异常并将其存储在
FatalException
中的
之外。在Delphi 10.1 Berlinar上工作。是否确实有效?释放那个异常对象怎么样?这不起作用(至少在XE2中是这样)。我尝试了它,得到了一个非模式消息对话框,后面是一个错误。
OnTerminate
处理程序在
Execute()
退出后由工作线程上下文中的
Synchronize()
调用。如果在
Synchronize()
中引发异常,则它将获取该异常并在工作线程上下文中重新引发它。因此,在
OnTerminate
中引发异常是一件坏事。要正确执行此操作,您必须手动获取
FatalException
的所有权,以某种方式将
TThread.ffataleException
重置为nil(以便
TThread
不会销毁对象),并在退出
Synchronize()
后自己在主线程中重新引发它。黑客必须拥有所有权
procedure TForm1.onterm(Sender: TObject);
var
  ex: Exception;
begin
  Assert(Sender is TThread);
  ex := Exception(TThread(Sender).FatalException);
  if Assigned(ex) then
    // Thread terminated due to an exception
    raise ex;
  Dec(nrthd);
end;