Multithreading 在TThread Execute中引发异常?
我刚刚意识到我的异常不会在我的线程中显示给用户 首先,我在我的线程中使用了这个函数来引发异常,但它不起作用:Multithreading 在TThread Execute中引发异常?,multithreading,delphi,exception,raise,tthread,Multithreading,Delphi,Exception,Raise,Tthread,我刚刚意识到我的异常不会在我的线程中显示给用户 首先,我在我的线程中使用了这个函数来引发异常,但它不起作用: except on E:Exception do begin raise Exception.Create('Error: ' + E.Message); end; IDE向我显示异常,但我的应用程序没有 我四处寻找解决方案,我发现: 这两个都不适合我 这是我的线程单元: unit uCheckForUpdateThread; interface uses Window
except on E:Exception do
begin
raise Exception.Create('Error: ' + E.Message);
end;
IDE向我显示异常,但我的应用程序没有
我四处寻找解决方案,我发现:
这两个都不适合我
这是我的线程单元:
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.
unit-uCheckForUpdateThread;
接口
使用
Windows、IdBaseComponent、IdComponent、IdTCPConnection、IdTCPClient、,
IdHTTP、GlobalFuncs、类、HtmlExtractor、SysUtils、表单;
类型
TUpdaterThread=class(TThread)
私有的
文件抓取器:THtmlExtractor;
HTTP:TIdHttp;
阿普梅杰,
小修,
近似值:整数;
UpdateText:string;
VersionStr:string;
ExceptionContext:字符串;
feexception:异常;
程序处理异常;
程序同步更新;
程序同步完成;
公众的
构造函数创建;
受保护的
程序处理异常;事实上的
程序执行;推翻
结束;
实施
使用
乌敏;
{TUpdaterThread}
构造函数TUpdaterThread.Create;
开始
继承创建(False);
结束;
过程TUpdaterThread.Execute;
开始
继承;
FreeOnTerminate:=真;
如果终止,则
出口
FileGrabber:=THtmlExtractor.Create;
HTTP:=TIdHTTP.Create(nil);
尝试
尝试
文件抓取器http://jeffijoe.com/xSky/Updates/CheckForUpdates.php');
E上除外:例外
开始
UpdateText:=“更新xSky时出错!”;
ExceptionContext:='错误:找不到远程文件!请重新启动xSky并重试!另外,请确保您已连接到Internet,并且您的防火墙没有阻止xSky!';
手部异常;
结束;
结束;
尝试
AppMajor:=stroint(FileGrabber.ExtractValue('AppMajor[',']');
AppMinor:=stroint(FileGrabber.ExtractValue('AppMinor[',']');
AppRelease:=stroint(FileGrabber.ExtractValue('AppRelease[[',']'));
E上除外:例外
开始
手部异常;
结束;
结束;
如果(应用程序版本大调<应用程序大调)或(应用程序版本小调<应用程序小调)或(应用程序版本发布<应用程序大调),则
开始
VersionStr:=格式(“%d.%d.%d”,[AppMajor,AppMinor,AppRelease]);
UpdateText:=“下载版本”+VersionStr;
同步(SyncUpdateBL);
结束;
最后
免费的;
HTTP.Free;
结束;
同步(SyncFinalize);
结束;
过程TUpdaterThread.SyncFinalize;
开始
DoTransition(frmMain.TransSearcher3,frmMain.gbLogin,True,500);
结束;
过程TUpdaterThread.SyncUpdateLbl;
开始
frmMain.lblCheckingForUpdates.Caption:=UpdateText;
结束;
过程TUpdaterThread.HandleException;
开始
FEException:=异常(ExceptObject);
尝试
同步(DoHandleException);
最后
FEException:=零;
结束;
结束;
过程TUpdaterThread.DoHandleException;
开始
应用程序。ShowException(feException);
结束;
结束。
如果你需要更多信息,请告诉我
同样:IDE捕获所有异常,但我的程序不显示它们
编辑:最终是Cosmin的解决方案起了作用-最初没有起作用的原因是,我没有添加ErrMsg变量,而是将该变量包含的任何内容放入了Synchronize中,这不会起作用,但我不知道为什么。我在没有其他想法的情况下意识到了这一点,我只是在处理解决方案时弄糟了是的
和往常一样,我是开玩笑的
没有你的源代码会很难,但我已经测试过了:
它很好用。也许你应该看看
编辑:
你没有按照你指出的链接告诉我们的去做。检查我的链接,你就会知道怎么做
编辑2:
试试看,告诉我它是否有效:
TUpdaterThread= class(TThread)
private
FException: Exception;
procedure DoHandleException;
protected
procedure Execute; override;
procedure HandleException; virtual;
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
HandleException;
End;
Try
AppMajor := StrToInt(FileGrabber.ExtractValue('AppMajor[', ']'));
AppMinor := StrToInt(FileGrabber.ExtractValue('AppMinor[', ']'));
AppRelease := StrToInt(FileGrabber.ExtractValue('AppRelease[[', ']'));
Except
HandleException;
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.HandleException;
begin
FException := Exception(ExceptObject);
try
Synchronize(DoHandleException);
finally
FException := nil;
end;
end;
procedure TMyThread.DoHandleException;
begin
Application.ShowException(FException);
end;
线程不会自动将异常传播到其他线程中,所以您必须自己处理它 Rafael概述了一种方法,但也有其他方法。Rafael指出的解决方案是通过将异常编组到主线程中来同步处理异常 在我自己使用的线程池中,线程捕获并接管异常的所有权。这允许控制线程随心所欲地处理它们 代码如下所示
procedure TMyThread.Execute;
begin
Try
DoStuff;
Except
on Exception do begin
FExceptAddr := ExceptAddr;
FException := AcquireExceptionObject;
//FBugReport := GetBugReportCallStackEtcFromMadExceptOrSimilar.
end;
End;
end;
如果控制线程选择引发异常,它可以这样做:
raise Thread.FException at Thread.FExceptAddr;
有时您可能有无法调用Synchronize的代码,例如某些DLL,这种方法很有用
请注意,如果不引发捕获的异常,则需要销毁该异常,否则将导致内存泄漏。以下是我对该问题的非常简短的“看法”。它仅适用于Delphi 2010+(因为该版本引入了匿名方法).与已经发布的更复杂的方法不同,我的方法只显示错误消息,没有更多,也没有更少
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;
我以前使用过使用TWMCopyData的SendMessge进行线程间通信,因此我认为以下方法应该有效:
Const MyAppThreadError = WM_APP + 1;
constructor TUpdaterThread.Create(ErrorRecieverHandle: THandle);
begin
Inherited Create(False);
FErrorRecieverHandle := Application.Handle;
end;
procedure TUpdaterThread.Execute;
var
cds: TWMCopyData;
begin
try
DoStuff;
except on E:Exception do
begin
cds.dwData := 0;
cds.cbData := Length(E.message) * SizeOf(Char);
cds.lpData := Pointer(@E.message[1]);
SendMessage(FErrorRecieverHandle, MyAppThreadError, LPARAM(@cds), 0);
end;
end;
end;
我只使用它来发送简单的数据类型或字符串,但我相信它可以根据需要进行调整,通过发送更多信息
您需要在线程创建的表单中向构造函数添加Self.Handle
,并在创建它的表单中处理消息
procedure HandleUpdateError(var Message:TMessage); message MyAppThreadError;
var
StringValue: string;
CopyData : TWMCopyData;
begin
CopyData := TWMCopyData(Msg);
SetLength(StringValue, CopyData.CopyDataStruct.cbData div SizeOf(Char));
Move(CopyData.CopyDataStruct.lpData^, StringValue[1], CopyData.CopyDataStruct.cbData);
Message.Result := 0;
ShowMessage(StringValue);
end;
关于多层次开发,您需要了解一些非常重要的内容: 每个线程都有自己的调用堆栈,几乎就像它们是独立的程序一样。这包括程序的主线程 线程只能以特定方式相互交互:
- 他们可以对共享数据或对象进行操作。这可能会导致并发问题“竞争条件”,因此您需要能够帮助他们“很好地共享数据”。这就引出了下一点
- 他们可以通过usi“相互发出信号”
procedure HandleUpdateError(var Message:TMessage); message MyAppThreadError; var StringValue: string; CopyData : TWMCopyData; begin CopyData := TWMCopyData(Msg); SetLength(StringValue, CopyData.CopyDataStruct.cbData div SizeOf(Char)); Move(CopyData.CopyDataStruct.lpData^, StringValue[1], CopyData.CopyDataStruct.cbData); Message.Result := 0; ShowMessage(StringValue); end;
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;