我只有线程方面的经验。我想并行读取一些基本数据库表,并且需要等到读取所有表后,程序才能合理地进行。在这方面,阻塞主线程对我来说还可以。

此代码(简化)可以正常工作:

procedure ReadDBMultiThread;
  var ATasks : Array of ITask;
begin
  SetLength(ATasks, 3);
  ATasks[0] := TTaskCreate(procedure() begin DB_ReadTable1; end);
  ATasks[1] := TTaskCreate(procedure() begin DB_ReadTable2; end);
  ATasks[2] := TTaskCreate(procedure() begin DB_ReadTable3; end);

  ATasks[0].Start;
  ATasks[1].Start;
  ATasks[2].Start;

  TTask.WaitForAll(ATasks);
end;

但是,假设我想更新主表单以显示进度,即已经读取了哪个数据库表(或执行任何其他必要的主线程工作)。显然,我不能使用Synchronise(),因为那样会导致WaitForall()死锁,而我不能使用Queue(),因为那样会在WaitForAll()完成后执行。

因此,是否有一个好的解决方案来解决这种“WaitForAll vs Synchronise”情况?我猜想,这肯定是许多人遇到的情况……需要等待所有任务完成,但是想更新主线程……

我考虑过用伪代码给出的类似这样的东西,代替了WaitForAll()语句:
repeat
  Applicaton.ProcessMessages; // or "ProcessSynchroniseMessages"
until "AllTaskCompleted"(ATasks);

这行得通吗?有更好的解决方案吗?

我可以编写自己的例程,例如ProcessMessages,但仅限于同步消息,即,直到稍后才执行其他主要形式的事件吗?

提前谢谢了!

最佳答案

TThread.Synchronize()TThread.Queue()不使用窗口消息(嗯,有一条消息“唤醒”主线程以发出挂起的请求信号,但是实际的同步本身不是基于消息的)。请求被放入一个全局队列中,主线程会检查它何时处于空闲状态,或者何时检测到“唤醒”消息。您可以通过直接调用Classes.CheckSynchronize()函数手动泵送相同的队列:

while not TTask.WaitForAll(ATasks, 1000) do
begin
  // process any pending TThread.Synchronize() and TThread.Queue() requests
  CheckSynchronize(0);
  // process any pending UI paint requests, but not other messages
  Application.MainForm.Update;
  // anything else you need...
end;

10-05 23:53