zoukankan      html  css  js  c++  java
  • Delphi thread exception mechanism

    http://www.techques.com/question/1-3627743/Delphi-thread-exception-mechanism

    i have a dilema on how threads work in delphi, and why at a moment when a thread should raise an exception,

    the exception is not showed. bellow is the code with comments, maybe somebody cand explain to me how that thread,

    or delphi, is managing access violations

    //thread code

    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.

    project's code

    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.

    the purpose of this application is only to know where the access violation is catch, and how the code should be written.
    i can not understand why in the line "a[2] := 'dbwdbkbckbk';" the AV is not raised.

    thanks in advance and best regards!

    hreading is one place where you should swallow exceptions.

    The gist of handling Exceptions in threads is that if you want the exception to be shown to the end user,

    you should capture it and pass it on to the main thread where it can safely be shown.

    You'll find some examples in this EDN thread 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;

    In Delphi 2005 — and probably most other versions — if an exception escapes from the Execute method without being handled,

    then it is caught by the function that called Execute and stored in the thread's FatalException property.

    (Look in Classes.pasThreadProc.)

    Nothing further is done with that exception until the thread is freed, at which point the exception is also freed.

    It's your responsibility, therefore, to check that property and do something about it.

    You can check it in the thread's OnTerminate handler.

    If it's non-null, then the thread terminated due to an uncaught exception. So, for example:

    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;

    We can also reraise FatalException.

    Reraising seems not logical but if you have an central exception/error handler in your code and

    and if you just want to include thread exceptions into that mechanisim, you can reraise on some rare situation :

    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;

    How to handle exceptions in TThread objects

    By: Corbin Dunn

    Abstract: This document describes how to properly handle and show an exception that happens in a thread.

    Question: 
    I have a TThread object which may raise an exception in the Execute procedure.

    When an exception is raised, I want to be able to show that exception to the end user.

    How do I go about doing this in the most efficient way? 

    Answer:
    With a TThread object, if you don't catch an exception in the Execute procedure of a TThread,

    you may get access violations.

    The Delphi IDE may break fine on the exception, but often when the application is run outside of the IDE

    you get an "Application error has occurred" exception and your application stops running.

    If you don't care about showing the end user that an exception occurred,

    you can simply wrap your Execute procedure with a try..finally block such as:

    procedure TMyThread.Execute;
    begin
      try
        // Do your thread stuff here
      except // Eat all exceptions
      end;
    end;

    Quite often, this isn't the best solution and you will want to show the message to the end user,

    or allow your application to further process the message.

    The easiest way to do this, is to add an Exception object to your TThread class,

    and call the appropriate handler based on the type of exception.

    Here is an example of how to do this.

    The project consists of one form with a Button placed on it:

    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Classes, 
      Graphics, Controls, Forms, Dialogs, StdCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
      TMyThread = class(TThread)
      private
        FException: Exception;
        procedure DoHandleException;
      protected
        procedure Execute; override;
        procedure HandleException; virtual;
      public
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.DFM}
    
    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;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      // Create an instance of the TMyThread
      with TMyThread.Create(True) do
      begin
        FreeOnTerminate := True;
        Resume; 
      end;
    end;
    
    end.

    vv

  • 相关阅读:
    小D课堂
    小D课堂
    小D课堂
    小D课堂
    小D课堂
    c++ , const对象中的变量不能被修改
    C++,常成员函数
    c++,给常成员变量赋值
    C++中,引用作为函数参数
    C++中,如何定义和使用指向成员函数的指针
  • 原文地址:https://www.cnblogs.com/shangdawei/p/4016429.html
Copyright © 2011-2022 走看看