当我使用AsyncCalls时,如何最好地将异常引入主线程?例如:
procedure TUpdater.needsToGoFirst();
begin
asyncCall := TAsyncCalls.Invoke(procedure
begin
//some stuff
if (possiblyTrueCondition = True) then
raise Exception.Create('Error Message');
//more stuff
TAsyncCalls.VCLSync(procedure
begin
notifyUpdates();
end);
end);
end;
procedure TUpdater.needsToGoSecond();
begin
asyncCall2 := TAsyncCalls.Invoke(procedure
begin
//initial stuff
asyncCall.Sync();
//stuff that needs to go second
TAsyncCalls.VCLSync(procedure
begin
notifyUpdates();
end);
end);
end;
我知道调用asycCall.Sync
将为我抛出异常,但由于我当前让我的线程通知主线程已经进行了更新的方式,我真的没有任何地方,我在主线程中调用Sync
。这样做也证明是困难的,因为我实际上有另一个调用Sync
的线程,以确保在获取第一个线程应该首先处理的资源之前设置了一些东西。
我是否需要使用try-catch包装这些函数的内部并使用VCLSync
来获取主线程的异常?是否有更好的方法来检查某种主线程空闲循环中的异常?
修改1
我的另一个想法是创建一个循环,其唯一的工作是检查异常的IAscynCall引用,并使用它来引发主线程的异常,而不是在每个帖子中复制该代码。 needsToGoSecond
metod可能仍会首先进入异常,但它会保留异常并且循环会捕获它。
答案 0 :(得分:1)
您声明以下内容:
我真的没有在主线程中调用Sync的任何地方。这样做也证明很难,因为我实际上有另一个调用Sync的线程。
...
我是否需要使用try-catch包装这些函数的内部并使用VCLSync自行获取主线程的异常?有没有更好的方法来检查某种主线程空闲循环中的异常?
在另一个线程中调用Sync
将在那里引发异常。如果你不想在那里提出它,如果它必须在主线程中引发,那么就没有选择。您只需在异步过程中自己捕获任何未处理的异常。然后,您可以将它们排队到主线程,可能是通过调用TThread.Queue
,发布Windows消息或某种类似的队列机制。另一种选择是使用VCLSync
,如果您不介意此时的同步。
底线是从另一个线程调用Sync
,并且需要在主线程上引发异常,这不是兼容的目标。你必须自己捕获异常并停止AsyncCalls处理它。
基本上,这只是对您当前方法的扩展。此时您的火灾通知主线程,而不是主线程同步。毕竟,我猜你正在使用异步方法,因为你不想从主线程同步。因此,扩展是您需要能够通知主线程的错误和异常,以及更正常的结果。
答案 1 :(得分:0)
根据要求,这是一个如何将线程内部的异常传递给主线程的示例。异常停止在线程中执行,然后我捕获错误并将其返回给用户。
这里有两个非常简单的类,一个包含在线程中运行的代码的worker类,以及一个线程类:
type
TMyWorker = class
private
FExceptionObject: Pointer;
FExceptionAddress: Pointer;
public
constructor Create; reintroduce;
destructor Destroy; override;
procedure _Execute;
procedure GetException;
end;
implementation
constructor TMyWorker.Create;
begin
inherited Create;
FExceptionObject := nil;
FExceptionAddress := nil;
end;
procedure TMyWorker._Execute;
begin
try
// run code
except
FExceptionObject := AcquireExceptionObject; // increments exception's refcnt
FExceptionAddress := ExceptAddr;
end;
end;
procedure TMyWorker.GetException;
begin
if Assigned(FExceptionObject) then
raise Exception(FExceptionObject) at FExceptionAddress; // decrements exception's refcnt
end;
destructor TMyWorker.Destroy;
begin
if Assigned(FExceptionObject) then
begin
ReleaseExceptionObject; // decrements exception's refcnt
FExceptionObject := nil;
FExceptionAddress := nil;
end;
inherited;
end;
type
TMyThread = class(TThread)
private
FWorker: TMyWorker;
protected
procedure Execute; override;
public
constructor Create(Worker: TMyWorker);
end;
implementation
procedure TMyThread.Execute;
begin
FWorker._Execute;
end;
constructor TMyThread.Create(Worker: TMyWorker);
begin
FWorker := Worker;
FreeOnTerminate := False;
inherited Create(False);
end;
然后我的主线程代码创建一个worker对象并将其传递给要执行的线程的构造函数。执行完成后,主线程会检查并重新引发任何异常。
var
myWorker: TMyWorker;
begin
myWorker := TMyWorker.Create;
try
with TMyThread.Create(myWorker) do
begin
WaitFor; // stop execution here until the thread has finished
Free; // frees the thread object
end;
myWorker.GetException; // re-raise any exceptions that occurred while thread was running
finally
FreeAndNil(myWorker);
end;
end;
您还可以查看此EDN文章:
答案 2 :(得分:0)
所以最后,我提出了以下适合我需求的解决方案,我只想让GUI显示任何异常消息而不管线程。如果你想更具体地管理错误,我会推荐大卫给出的答案。
首先,使用asyncCall.Sync();
不正确。我现在使用TEvent
个对象来等待有问题的实际事件发生。然后,线程可以继续执行其他工作,而不会使等待线程等待的时间超过所需的时间。
其次,我现在使用循环来捕获发生的异常并将错误同步回主线程。例如,一个线程可能如下所示:
procedure TUpdater.needsToGoSecond();
begin
fAsyncThreads.Add(TAsyncCalls.Invoke(procedure
begin
//initial stuff
myEvent.Wait();
//stuff that needs to go second
if (possiblyTrueCondition = True) then
raise Exception.Create('Error Message');
TAsyncCalls.VCLSync(procedure
begin
notifyUpdates();
end);
end));
end;
我有另一个线程在其他地方捕捉和提出例外:
procedure TUpdater.catchExceptions();
begin
fAsyncCatchExceptions := TAsyncCalls.Invoke(procedure
var
asyncThread: IAsyncCall;
errorText: string;
begin
while(true)do
begin
for asyncThread in fAsyncThreads do
begin
if(Assigned(asyncThread)) and (asyncThread.Finished)then
begin
try
asyncThread.Sync();
except on E: Exception do
begin
errorText := E.Message;
TAsyncCalls.VCLInvoke(procedure begin raise Exception.Create(errorText); end);
end;
end;
fAsyncThreads.Remove(asyncThread);
end;//if
end;//for
Sleep(2000);
end;//while
end);
end;
似乎必须通过VCLInvoke(或TThread.Queue)调用而不是VCLSync(或TThread.Synchronize)调用抛出异常。在同步时,我认为外部AsyncCall捕获异常并阻止GUI显示它。此外,我的catch循环还没有创建在主线程中引发的异常的副本。因为您似乎需要对raise命令进行排队,所以无法重新引发当前异常。您将获得访问冲突,因为它最希望在GUI到达时清理它。
答案 3 :(得分:-1)
好吧,自从我们来到inexact answers后,还有一个。
OmniThreadLibrary:http://otl.17slon.com/
论坛(作者在那里非常敏感):http://otl.17slon.com/forum/
异步示例:http://www.thedelphigeek.com/2012/07/asyncawait-in-delphi.html
预订示例:http://samples.leanpub.com/omnithreadlibrary-sample.pdf
有关OTL例外情况的更多信息:http://www.thedelphigeek.com/2011/07/life-after-21-exceptions-in.html