Delphi线程异常机制

时间:2010-09-02 13:51:11

标签: multithreading delphi exception-handling

我对delphi中的线程如何工作存在困境,为什么在线程应该引发异常的时候,没有显示异常。 bellow是带注释的代码,也许有人会向我解释该线程或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没有提出。

4 个答案:

答案 0 :(得分:20)

在Delphi 2005中 - 可能还有大多数其他版本 - 如果异常在没有被处理的情况下从Execute方法中逃脱,那么它会被调用Execute并存储在线程FatalException中的函数捕获。 1}}属性。 (查看 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;

不需要互锁功能来跟踪您的线程数。线程创建函数和终止处理程序都始终在主线程的上下文中运行。简单的旧IncDec就足够了。

答案 1 :(得分:12)

线程应该吞下例外的地方。

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

您可以在此EDN主题How to Handle exceptions in TThread Objects中找到一些示例。

procedure TMyThread.DoHandleException;
begin
  // Cancel the mouse capture
  if GetCapture <> 0 then SendMessage(GetCapture, WM_CANCELMODE, 0, 0);
  // Now actually show the exception
  if FException is Exception then
    Application.ShowException(FException)
  else
    SysUtils.ShowException(FException, nil);
end;

procedure TMyThread.Execute;
begin
  FException := nil;
  try
    // raise an Exception
    raise Exception.Create('I raised an exception');
  except
    HandleException;
  end;
end;

procedure TMyThread.HandleException;
begin
  // This function is virtual so you can override it
  // and add your own functionality.
  FException := Exception(ExceptObject);
  try
    // Don't show EAbort messages
    if not (FException is EAbort) then
      Synchronize(DoHandleException);
  finally
    FException := nil;
  end;
end;

答案 2 :(得分:0)

我们还可以重新加载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;

答案 3 :(得分:0)

“ a”变量未初始化!它可以指向计算机中任何可能的内存位置。它甚至可以指向物理上不存在的位置(即使由于虚拟内存系统而没有意义)。

因此,在您的程序中,如果“ a”偶然指向有效的内存地址(我的意思是该进程拥有的地址),则您的代码将在该位置写入而不会发生访问冲突。我认为您至少应在NIL上加上“ a”。

在此处查看人头马(Remy Lebeau)的评论:http://www.stackoverflow.com/a/16071764/46207
而且这也是:Why uninitialized pointers cause mem access violations close to 0?