首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >是否在TThread执行中引发异常?

是否在TThread执行中引发异常?
EN

Stack Overflow用户
提问于 2011-03-26 22:36:32
回答 6查看 4.5K关注 0票数 8

我刚刚意识到我的异常并没有在我的线程中显示给用户!

一开始,我在线程中使用它来引发异常,但这并不起作用:

代码语言:javascript
运行
复制
except on E:Exception do
begin
  raise Exception.Create('Error: ' + E.Message);
end;

IDE会向我显示异常,但我的应用程序不会!

我四处寻找解决方案,这是我发现的:

Delphi thread exception mechanism

http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22039681.html

这两种方法对我都不起作用。

这是我的线程单元:

代码语言:javascript
运行
复制
unit uCheckForUpdateThread;

interface

uses
  Windows, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdHTTP, GlobalFuncs, Classes, HtmlExtractor, SysUtils, Forms;

type
  TUpdaterThread = class(TThread)
  private
    FileGrabber : THtmlExtractor;
    HTTP : TIdHttp;
    AppMajor,
    AppMinor,
    AppRelease : Integer;
    UpdateText : string;
    VersionStr : string;
    ExceptionText : string;
    FException: Exception;
    procedure DoHandleException;
    procedure SyncUpdateLbl;
    procedure SyncFinalize;
  public
    constructor Create;

  protected
    procedure HandleException; virtual;

    procedure Execute; override;
  end;

implementation

uses
  uMain;

{ TUpdaterThread }

constructor TUpdaterThread.Create;
begin
  inherited Create(False);
end;

procedure TUpdaterThread.Execute;
begin
  inherited;
  FreeOnTerminate := True;

  if Terminated then
    Exit;

  FileGrabber           := THtmlExtractor.Create;
  HTTP                  := TIdHTTP.Create(nil);
  try
    try
      FileGrabber.Grab('http://jeffijoe.com/xSky/Updates/CheckForUpdates.php');
    except on E: Exception do
    begin
      UpdateText := 'Error while updating xSky!';
      ExceptionText := 'Error: Cannot find remote file! Please restart xSky and try again! Also, make sure you are connected to the Internet, and that your Firewall is not blocking xSky!';
      HandleException;
    end;
    end;

    try
      AppMajor      := StrToInt(FileGrabber.ExtractValue('AppMajor[', ']'));
      AppMinor      := StrToInt(FileGrabber.ExtractValue('AppMinor[', ']'));
      AppRelease    := StrToInt(FileGrabber.ExtractValue('AppRelease[[', ']'));
    except on E:Exception do
    begin
      HandleException;
    end;
    end;

    if (APP_VER_MAJOR < AppMajor) or (APP_VER_MINOR < AppMinor) or (APP_VER_RELEASE < AppRelease) then
    begin
      VersionStr := Format('%d.%d.%d', [AppMajor, AppMinor, AppRelease]);
      UpdateText := 'Downloading Version ' + VersionStr;
      Synchronize(SyncUpdateLbl);
    end;

  finally
    FileGrabber.Free;
    HTTP.Free;
  end;
  Synchronize(SyncFinalize);
end;

procedure TUpdaterThread.SyncFinalize;
begin
  DoTransition(frmMain.TransSearcher3, frmMain.gbLogin, True, 500);
end;

procedure TUpdaterThread.SyncUpdateLbl;
begin
  frmMain.lblCheckingForUpdates.Caption := UpdateText;
end;

procedure TUpdaterThread.HandleException;
begin
  FException := Exception(ExceptObject);
  try
    Synchronize(DoHandleException);
  finally
    FException := nil;
  end;
end;

procedure TUpdaterThread.DoHandleException;
begin
  Application.ShowException(FException);
end;

end.

如果您需要更多信息,请让我知道。

再说一次: IDE捕获所有异常,但我的程序不显示它们。

编辑:它是Cosmin的解决方案,最终起作用了--一开始没有起作用的原因,是因为我没有添加ErrMsg变量,而是将变量包含的任何内容放入同步中,这是不起作用的,但是我不知道为什么。当我没有其他想法时,我意识到了这一点,我只是胡乱摆弄解决方案。

一如既往,笑话都在我身上。=P

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2011-03-27 00:41:33

这是我在这个问题上非常非常简短的“观点”。它只适用于Delphi 2010+ (因为该版本引入了匿名方法)。与已经发布的更复杂的方法不同,我的方法只显示错误消息,没有更多,也没有更少。

代码语言:javascript
运行
复制
procedure TErrThread.Execute;
var ErrMsg: string;
begin
  try
    raise Exception.Create('Demonstration purposes exception');
  except on E:Exception do
    begin
      ErrMsg := E.ClassName + ' with message ' + E.Message;
      // The following could be all written on a single line to be more copy-paste friendly  
      Synchronize(
        procedure
        begin
          ShowMessage(ErrMsg);
        end
      );
    end;
  end;
end;
票数 9
EN

Stack Overflow用户

发布于 2011-03-27 01:27:11

关于多线程开发,你需要了解一些非常重要的事情:

每个线程都有自己的调用堆栈,就像它们是独立的程序一样,几乎是的。这包括你的程序的主线程。

线程之间只能以特定的方式进行交互:

  • 它们可以对共享数据或对象进行操作。这可能会导致并发问题“竞争条件”,因此您需要能够帮助他们“很好地共享数据”。这就引出了下一点。
  • ,它们可以使用各种操作系统支持例程来“相互发送信号”。其中包括:
    • Mutexes
    • Critical Sections
    • Events

  • ,最后你可以向其他线程发送消息。前提是该线程以某种方式被编写为消息接收者。

NB:请注意,严格地说,线程不能直接调用其他线程。例如,如果线程A试图直接调用线程B,那么这将是线程A调用堆栈上的一个步骤!

这就把我们带到了这个问题的主题:“在我的线程中没有抛出异常”

这样做的原因是,异常所做的一切都是:

  • 记录错误
  • 并展开调用堆栈。<-- NB:您的TThread实例不能展开主线程的调用堆栈,也不能任意中断主线程的执行。

因此,TThread不会向主应用程序automatically报告异常。

您必须明确地决定您希望如何处理线程中的错误,并相应地实现。

解决方案

  • 第一步与在单线程应用程序中相同。你需要决定这个错误意味着什么,以及线程应该如何反应。线程是否应该继续执行abort?
  • Should logged/reported?
  • Does now.

  • ?<--这是到目前为止最难实现的,所以我们将跳过它

一旦决定了这一点,实现适当的执行handler.

  • TIP: Make sure the exception doesn't escape the thread. The OS won't like you if it does.

  • If你需要主程序(
  • )来向用户报告错误,你有几个选择。
    • 如果线程是用来返回结果对象的,那么很简单:做一个更改,这样如果出现错误,它就可以在该对象中返回错误。
    • 会向主线程发送一条消息来报告错误。请注意,主线程已经实现了一个消息循环,因此应用程序在处理该message.

时将立即报告错误

编辑:指定需求的代码示例。

如果你想要做的就是通知用户,那么Cosmind Prund's answer在Delphi2010上应该能完美地工作。老版本的Delphi需要做更多的工作。以下代码在概念上类似于Jeff's own answer,但没有错误:

代码语言:javascript
运行
复制
procedure TUpdaterThread.ShowException;
begin
  MessageDlg(FExceptionMessage, mtError, [mbOk], 0);
end;

procedure TUpdaterThread.Execute;
begin
  try

    raise Exception.Create('Test Exception');
    //The code for your thread goes here
    //
    //

  except
    //Based on your requirement, the except block should be the outer-most block of your code
    on E: Exception do
    begin
      FExceptionMessage := 'Exception: '+E.ClassName+'. '+E.Message;
      Synchronize(ShowException);
    end;
  end;
end;

对Jeff自己的答案进行了一些重要的更正,包括他的问题中显示的实现:

只有当您的线程在while not Terminated do中实现时,对Terminate的调用才是相关的……循环。看一看Terminate方法的实际作用。

调用Exit是一种不必要的浪费,但您这样做可能是因为您的下一个错误。

在您的问题中,您将每个步骤包装在其自己的try...except中以处理异常。这是一个的绝对禁忌!通过这样做,您可以假装即使发生了异常,一切都是正常的。您的线程尝试下一步,但实际上肯定会失败!这是而不是处理异常的方式!

票数 13
EN

Stack Overflow用户

发布于 2011-03-26 23:00:22

线程不会自动将异常传播到其他线程中。所以你必须自己处理它。

Rafael概述了一种方法,但也有替代方法。Rafael指出的解决方案是通过将异常编组到主线程中来同步处理异常。

在我自己对线程的一个使用中,线程池,线程捕获并接管异常的所有权。这允许控制线程随心所欲地处理它们。

代码如下所示。

代码语言:javascript
运行
复制
procedure TMyThread.Execute;
begin
  Try
    DoStuff;
  Except
    on Exception do begin
      FExceptAddr := ExceptAddr;
      FException := AcquireExceptionObject;
      //FBugReport := GetBugReportCallStackEtcFromMadExceptOrSimilar.
    end;
  End;
end;

如果控制线程选择引发异常,它可以这样做:

代码语言:javascript
运行
复制
raise Thread.FException at Thread.FExceptAddr;

有时你的代码可能无法调用同步,例如一些DLL,这种方法很有用。

请注意,如果不引发捕获的异常,则需要销毁该异常,否则会发生内存泄漏。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5442978

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档