delphi 执行任务中操作的例程,显示加载屏幕,而不冻结主窗体

u0sqgete  于 2024-01-07  发布在  其他
关注(0)|答案(1)|浏览(321)

我希望构建一个例程,负责通过过程(Tproc)使用任务执行任何操作,同时显示加载屏幕,而不会冻结系统的主窗体。一个额外的细节是,它还能够捕获操作可能产生的可能异常。
我构建了下面的代码,它工作得很好,但有时会出错,加载屏幕最终无法关闭,始终存在于屏幕上。
任何建议都欢迎。谢谢。

  1. type
  2. TLoadingClass= class
  3. strict private
  4. class var FForm: TForm;
  5. public
  6. class procedure ActionAndWait(Action: Tproc);
  7. end;
  8. class procedure TLoadingClass.ActionAndWait(Action: Tproc);
  9. var
  10. aTask: ITask;
  11. vException: Pointer;
  12. begin
  13. vException := nil;
  14. FForm := TLoadingForm.Create(nil);
  15. try
  16. aTask := TTask.Run(procedure
  17. begin
  18. try
  19. try
  20. Action; {Run Action}
  21. except on E: Exception do
  22. vException := @E {Capture Exception}
  23. end
  24. finally
  25. while not FForm.Showing do {Wait for the form to be created, if the action is very quick.}
  26. Sleep(1);
  27. TLoadingForm(FForm).Hide;
  28. TLoadingForm(FForm).Close;
  29. end;
  30. end);
  31. TLoadingForm(FForm).ShowModal; {Show the loading form}
  32. finally
  33. TTask.WaitForAll(aTask);
  34. FreeAndNil(FForm);
  35. if Assigned(vException) then
  36. raise Exception(@vException);
  37. end;
  38. end;

字符串
调用示例

  1. TLoadingClass.ActionAndWait(
  2. procedure
  3. begin
  4. try
  5. Sleep(5000);
  6. raise Exception.Create('Test');
  7. except on E: Exception do
  8. ShowMessage(E.Message);
  9. end;
  10. end);

y0u0uwnf

y0u0uwnf1#

你的代码有几个问题。
首先,你从后台线程访问UI(表单),这是你永远不应该做的。这样的代码总是需要与主线程同步。
接下来,你没有正确处理任务异常。异常对象是由编译器自动处理的,你不能只是抓取一个指向异常对象的指针并在以后使用它。整个try...except在任务方法中是无用的。如果任务中有未处理的异常,WaitWaitForAll将引发EAggregatedException,您可以在那里捕获该异常并处理其内部异常。如果有多个任务引发异常,则会有多个内部异常。
接下来,您已经在传递给ActionAndWait的匿名方法中捕获了异常,因此捕获的异常不会作为EAgreggatedException传播。ActionAndWait中的ShowMessage将在后台线程的上下文中运行,如果您想从那里使用UI,它还需要与主线程同步。
您在TLoadingClass中将FForm声明为字段。最好删除该字段并使用本地变量,就像您在任务中使用的那样。当您调用HideClose时,也没有必要将FForm类型转换为TLoadingForm。此外,调用Close就足够了,因为它也会隐藏表单。
经过清理和更正的代码看起来像这样:

  1. class procedure TLoadingClass.ActionAndWait(Action: Tproc);
  2. var
  3. LTask: ITask;
  4. LForm: TLoadingForm;
  5. begin
  6. try
  7. LTask := TTask.Create(procedure
  8. begin
  9. try
  10. Action;
  11. finally
  12. TThread.Queue(nil,
  13. procedure
  14. begin
  15. LForm.Close;
  16. end);
  17. end;
  18. end);
  19. LForm := TLoadingForm.Create(nil);
  20. try
  21. try
  22. LTask.Start;
  23. LForm.ShowModal;
  24. finally
  25. TTask.WaitForAll(LTask);
  26. end;
  27. finally
  28. LForm.Free;
  29. end;
  30. except
  31. on E: EAggregateException do
  32. for var i := 0 to E.Count - 1 do
  33. ShowMessage(E.InnerExceptions[i].Message);
  34. on E: Exception do
  35. ShowMessage(E.Message);
  36. end;
  37. end;

字符串
调用示例:

  1. TLoadingClass.ActionAndWait(
  2. procedure
  3. begin
  4. Sleep(5000);
  5. raise Exception.Create('Test');
  6. end);

展开查看全部

相关问题