如何在 GUI 线程中调用 Thread.Execute?

How Do I Call a Thread.Execute in GUI Thread?

有一个 TThread 后代 class 用它自己的 Execute 方法做一些数学运算。它工作正常,但我正在寻找以下类型的优化。程序的 GUI 线程和上下文确定要创建、运行 和释放的这些线程的必要实例数。在某些(罕见或用户确定的)情况下,创建一个实例就足够了。

目前我使用以下结构(伪代码):

if ThreadsCount>1 then
  begin
    Creation of threads
    starting them
    waiting for results
    evaluating and assigning the best result
    freeing the threads
  end
else
  starting the math procedure (edited here separately)

// and in MyThread class declaration
procedure Execute... (edited here separately)

所以代码中有两个地方有我的数学过程,如果应用了一些数学更改,我必须编辑它们。 GUI 数学过程与在线程中调用的有点不同,所以我不能简单地提取方法并调用它。

请问有没有办法在GUI线程中创建线程实例并调用其Execute方法?

解决此问题的方法是将需要在工作线程或主线程中执行的代码提取到一个方法中。然后,您可以从工作线程的 Execute 方法或主线程代码中调用该代码。

您可以编写一些严重的 hacky、难以形容的糟糕代码,使您能够安全地调用 TThreadExecute()。但这是一件荒谬的事情。 TThread class 的全部意义在于:

  • 在 OS 中启动一个新线程;
  • 然后在该线程上调用 Execute()

所以:

  1. 如果您不需要线程,那么启动您不想使用的线程绝对没有意义
  2. 您需要阻止 Execute() 对其 线程进行任何处理-运行.
  3. 然后您可以从主线程调用 Execute
  4. 但是因为你不能保证线程需要多长时间才能在它调用Execute()时做任何处理 ,您仍然需要等待 线程 完成才能销毁 TThread 对象。

The GUI math procedure is a bit different from that one called in thread so I can not simply extract the method and call it.

这完全没有意义。

如果您的两个 "math procedures" 不同,那么尝试从 GUI 调用 线程实现 会改变程序的行为。相反,如果你可以重用线程实现,那么你肯定 还可以提取方法! (或者至少是共同元素。)


注意

也就是说,在 TThread.Execute() 中共享可能 运行 的代码时需要谨慎。任何必须在主线程上 运行 的代码都需要同步或排队。在 TThread 对象中,您只需调用 Synchronize()Queue() 方法。但是,共享代码不应位于 TThread 对象上,这会使事情变得有点棘手。

要解决此问题,您可以在 TThread 上使用 Synchronize()Queue() class 方法。这允许您在不实例化 TThread 实例的情况下进行同步。 (请注意,从主线程调用这些方法是安全的,因为在这种情况下它们会直接调用同步方法。)

以下几行代码应该可以解决问题。

在合适的对象中实现您的共享代码。这在概念上是一个 运行nable 对象,你可能想研究一下。

TSharedProcess = class
private
  { Set this if the process is run from a child thread,
    leave nil if run from main thread. }
  FThread: TThread;
  procedure SyncProc();
public
  procedure Run();
  property Thread: TThread read FThread write FThread;
end;

procedure TSharedProcess.Run();
begin
  ...
  TThread.Synchronize(FThread, SyncProc);
  ...
end;

当你想运行从主线程共享代码时,下面是一个选项。

begin
  LProc := TSharedProcess.Create(...);
  try
    LProc.Run();
  finally
    LProc.Free;
  end;
end;

从子线程到 运行 一个简单的线程包装器就足够了。然后你可以在主线程中创建 运行nable 对象,并将其传递给线程包装器。

{ TShardProcessThread for use when calling from child thread. }

constructor TSharedProcessThread.Create(AProc: TSharedProcessThread);
begin
  FProc := AProc;
  FProc.Thread := Self;
  inherited;
end;

procedure TShardProcessThread.Execute();
begin
  FProc.Run();
end;

{ Main thread creates child thread }
begin
  { Keep reference to FProc because it can only be destroyed after
    thread terminates. 
    TIP: Safest would be to use a reference counted interface. }
  FProc := TSharedProcess.Create(...);
  try
    LThread := TShardProcessThread.Create(FProc);
    LThread.OnTerminate := HandleThreadTerminate;
  except
    { Exception in thread create means thread will not run and
     will not terminate; so free object immediately. }
    FProc.Free;
    raise;
  end;
end;

免责声明

我没有测试过这段代码,因为我认为这样做没有任何好处。通过在主线程上强制代码到 运行,用户将一无所获。此外,同步代码的范例与异步代码根本不同。尝试实现混合通过使用 技术细节 .

使您的 'business code' 混乱来降低可维护性

使用风险自负。