【问题标题】:How Do I Call a Thread.Execute in GUI Thread?如何在 GUI 线程中调用 Thread.Execute?
【发布时间】:2018-02-06 08:48:55
【问题描述】:

有一个 TThread 后代类,它有自己的 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方法?

【问题讨论】:

  • 将数学放在一个单独的过程中,并从两个地方调用它!你想错了。
  • 谢谢@Dsm 这种方式很明显。但是,正如我在 Q 中所说的那样,我想避免它。理论上是否可以从 GUI 线程调用 Execute?
  • @asd-tm 不,不是。您需要将数学代码提取到一个函数中,该函数可以在任何需要它的线程中调用。你说你想避免这种情况,但你没有说为什么。但这是正确的解决方案

标签: multithreading delphi lazarus freepascal


【解决方案1】:

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

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

所以:

  1. 如果您不需要线程,则绝对没有意义启动您不想使用的线程。
  2. 您需要阻止Execute() 对其线程运行进行任何处理。
  3. 然后您可以从主线程调用Execute
  4. 但由于您无法保证线程在调用Execute()进行任何处理需要多长时间,所以您仍然需要等待在销毁TThread 对象之前完成线程

GUI 数学过程与在线程中调用的过程有点不同,所以我不能简单地提取方法并调用它。

这完全没有意义。

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


注意

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

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

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

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

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;

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

{ 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;

免责声明

我没有测试过这段代码,因为我认为做这样的事情没有任何好处。通过强制代码在主线程上运行,用户一无所获。此外,同步代码的范例与异步代码根本不同。尝试实现混合会降低可维护性,因为您的“业务代码”与技术细节混杂在一起。

使用风险自负。

【讨论】:

  • :D 希望我能说服 OP 相信这种方法的愚蠢。不过,在仔细检查TThread 实现的某些细节方面,这是一个有趣的练习。
  • 我猜想 OP 在“GUI 数学”和“线程数学”之间的细微差别可能与线程实现所需的同步有关。这可以解释为什么 OP 很乐意重用它,但没有找到提取它的方法。 (典型的 XY 问题。)这就是让我思考如何让提取的代码能够从主线程或子线程同步的原因。从这个角度来看,这是一个有趣的挑战。但仍然是我不会做的事情。
【解决方案2】:

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

【讨论】:

  • 感谢大卫的回复。但正如我在问题中所说,出于以下原因,我想避免这种方式:从线程和 GUI 线程中调用时,过程略有不同。例如,我以不同的方式与 GUI 控件交互,检查用户是否中止任务等。换句话说。理论上是否可以在 GUI 线程中调用预先设计好的 TThread.Execute?
  • 这完全没有意义。你完全可以按照我说的方式去做,这才是正确的做法。
  • 我不知道为什么这个问题被否决了,就像没有人知道谁投票一样
  • 不,不是。提取Execute方法中的所有代码也很简单。我认为这是在这里完成的。
  • @asd-tm 只需将代码提取到一个函数中,并传递一个参数指示哪个线程正在调用它,或者让它比较GetCurrentThreadIdMainThreadID,这样它就可以调整其逻辑相应。为函数提供一个回调,它可以定期调用以检查中止状态,然后为不同类型的线程使用不同的回调。将函数引用传递给变量以使其起作用。等等。有一些方法可以使函数在不同的线程中执行不同的操作,而无需实际绑定到任何特定线程。
猜你喜欢
  • 1970-01-01
  • 2012-11-19
  • 1970-01-01
  • 1970-01-01
  • 2020-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多