gpt4 book ai didi

multithreading - 确保所有 TThread.Queue 方法在线程自毁之前完成

转载 作者:行者123 更新时间:2023-12-03 14:47:22 33 4
gpt4 key购买 nike

我发现,如果使用 TThread.Queue 排队的方法调用调用 TApplication.WndProc 的方法(例如 ShowMessage ),则允许在原始方法完成之前运行后续排队的方法。更糟糕的是,它们似乎没有按 FIFO 顺序调用。

[编辑:实际上它们确实按照 FIFO 顺序启动。对于 ShowMessage,看起来后面一个先运行,因为在对话框出现之前调用了 CheckSynchronize。这会将下一个方法取消排队并运行它,直到后一个方法完成后才返回。然后才会出现该对话框。]

我试图确保所有从工作线程排队并在 VCL 线程中运行的方法都按照严格的 FIFO 顺序运行,并且它们都在工作线程被销毁之前完成。

我的另一个限制是我试图保持 GUI 与业务逻辑的严格分离。本例中的线程是业务逻辑层的一部分,因此我无法使用 PostMessage 处理程序中的 OnTerminate 来安排线程被销毁(正如其他地方的许多贡献者所建议的那样)。因此,我在 TThread.Execute 退出之前在最终排队方法中设置 FreeOnTerminate := True 。 (因此需要它们按照严格的 FIFO 顺序执行。)

这就是我的 TThread.Execute 方法的结束方式:

finally
// Queue a final method to execute in the main thread that will set an event
// allowing this thread to exit. This ensures that this thread can't exit
// until all of the queued procedures have run.
Queue(
procedure
begin
if Assigned(fOnComplete) then
begin
fOnComplete(Self);
// Handler sets fWorker.FreeOnTerminate := True and fWorker := nil
end;
SetEvent(fCanExit);
end);
WaitForSingleObject(fCanExit, INFINITE);
end;

但正如我所说,这不起作用,因为此排队方法在一些早期排队方法之前执行。

任何人都可以建议一种简单而干净的方法来完成这项工作,或者一个简单而干净的替代方案吗?

[到目前为止,我想出的唯一保持关注点分离和模块化的想法是给我的 TThread 子类一个自己的 WndProc 。然后我可以直接使用 PostMessage 到这个 WndProc 而不是主窗体。但我希望有更轻量级的东西。]

<小时/>

感谢迄今为止的回答和评论。我现在明白,上面带有排队的 SetEventWaitForSingleObject 的代码在功能上相当于在末尾调用 Synchronize 而不是 Queue,因为 QueueSynchronize 共享相同的队列。我首先尝试了 Synchronize,但失败的原因与上面的代码失败的原因相同 - 较早的排队方法会调用消息处理,因此最终的 Synchronize 方法会在较早的排队方法完成之前运行。

所以我仍然坚持原来的问题,现在归结为:我能否干净地确保所有排队的方法在释放工作线程之前已完成,以及我能否在不使用 PostMessage 的情况下干净地释放工作线程,这需要一个窗口句柄来发布(我的业务层无权访问)。

我还更新了标题以更好地反射(reflect)原始问题,尽管我很高兴有一个不使用 TThread.Queue 的替代解决方案(如果合适)。如果有人能想出更好的标题,请编辑它。

<小时/>

另一个更新:David Heffernan 的 This answer 建议在一般情况下,如果 PostMessage 不可用或不合适,则将 AllocateHWnd 与特殊的 TThread.Queue 一起使用。值得注意的是,在主窗体中使用 PostMessage 永远都不安全,因为窗口可以自发地重新创建并更改其句柄,这将导致旧句柄的所有后续消息丢失。这为我采用这个特定的解决方案提供了强有力的论据,因为在我的情况下创建隐藏窗口没有额外的开销,因为任何使用 PostMessage 的应用程序都应该这样做 - 即我的关注点分离论点是无关紧要的。

最佳答案

TThread.Queue() 是一个 FIFO 队列。事实上,它与 Thread.Sychronize() 使用相同的队列。但您是正确的,消息处理确实会导致排队方法执行。这是因为只要消息队列在处理新消息后空闲,TApplication.Idle() 就会调用 CheckSynchronize()。因此,如果排队/同步方法调用消息处理,则即使较早的方法仍在运行,也可以允许其他排队/同步方法运行。

如果您想确保在线程终止之前调用队列方法,您应该使用 Synchronize() 而不是 Queue(),或者使用 >OnTerminate 事件(由 Synchronize() 触发)。您在 finally block 中执行的操作实际上与 OnTerminate 事件本身已执行的操作相同。

排队方法中设置FreeOnTerminate := True会导致内存泄漏。当 Execute() 退出时,在调用 DoTerminate() 触发 OnTerminate 事件之前,立即评估 FreeOnTerminate (在我看来,这是一个疏忽,因为对它进行评估会阻止 OnTerminate 在终止时决定线程是否应该在 OnTerminate 退出后释放自身。因此,如果排队方法在 Execute() 退出后运行,则不能保证 FreeOnTerminate 会及时设置。在将控制权返回给线程之前等待排队方法完成正是Synchronize()的用途。 Synchronize() 是同步的,它等待方法退出。 Queue() 是异步的,它根本不等待。

关于multithreading - 确保所有 TThread.Queue 方法在线程自毁之前完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23359546/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com