我只对线程有经验。我想并行读取一些基本的数据库表,我需要等到所有表都被读取才能合理地进行。在这方面,阻止主线程对我来说是可以的。
此代码(简化)工作正常:
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;
但是,假设我想更新主窗体以显示进度,即已经读取了哪个数据库表(或者执行任何其他必要的主线程工作)。显然,我不能使用Synchronize(),因为这会导致WaitForall()的死锁,我不能使用Queue(),因为这将在WaitForAll()完成后执行。
那么,有没有一个很好的解决方案来解决这个“WaitForAll vs Synchronize”的情况?我想,这肯定是很多人进入的情况......需要等待所有人完成,但想要更新主线程......
我想到了这样的东西,在伪代码中给出,替换了WaitForAll()语句:
repeat
Applicaton.ProcessMessages; // or "ProcessSynchroniseMessages"
until "AllTaskCompleted"(ATasks);
这会有用吗?有更好的解决方案吗?
我可以编写一个像ProcessMessages这样的自己的例程,但仅限于同步消息,即直到以后才执行的其他主要表单事件?
非常感谢提前!
答案 0 :(得分:3)
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;