- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我发现,如果使用 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 而不是主窗体。但我希望有更轻量级的东西。]
感谢迄今为止的回答和评论。我现在明白,上面带有排队的 SetEvent
和 WaitForSingleObject
的代码在功能上相当于在末尾调用 Synchronize
而不是 Queue
,因为 Queue
和 Synchronize
共享相同的队列。我首先尝试了 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/
我试图在我的应用程序中进行长时间操作时显示一个 Activity ,该 Activity 指示覆盖(点的旋转的圆)。为此,我创建了一个具有TImage和Imagelist的无边界透明表单,我试图在主线
这是第一次尝试使用Threads,我正在尝试使用Thread复制目录,所以这就是我所做的(在我阅读 this post 之后): unit Unit1; interface uses Winapi
我有以下用 Delphi 2007 编写的服务,我发现它没有释放线程句柄。函数CurrentMemoryUsage和GetOpenHandles是返回应用程序使用的内存和句柄数量的函数。计时器每秒触发
情况。我创建了一个包含一些类的单元来解决代数问题(同余和系统),我向您展示代码: type TCongrError = class(Exception) end; type TCongruenc
据我了解,调用TThread的Synchronize将执行同步代码,就像在主线程中运行一样。假设在我的主线程中,我有一个按钮: procedure TForm3.Button1Click(Sender
我的应用程序是一个 tcp/ip 服务器,主线程仅创建一次并始终监听。当新的客户端连接时,主线程创建TClientThread类型的新线程。但是,没有正在运行的客户端线程的列表,因为这会使我的应用程序
我的程序中有一个主线程和一个单独的线程。如果单独的线程在主线程之前完成,它应该自动释放自身。如果主线程先完成,它应该释放单独的线程。 我了解 FreeOnTerminate,并且我读到您必须小心使用它
Delphi的TThread.OnTerminate帮助指出: The method assigned to the OnTerminate event is executed in the cont
我有一个 TThread 实例,我想等待用户输入。线程加载一些东西,等待用户点击一个按钮,然后继续它的任务。 我正在考虑将全局 bool 设置为 true,但我认为这对实例不太适用,并且线程必须在循环
我正在创建一个多线程应用程序,它创建了一个类 TThread 的动态数组,但对我来说神秘的是它会导致错误“访问冲突”到“创建” 代码形式: Unit UNT_Main; Interface Uses
我是 Windows 服务的新手,我需要完成这项工作...我的代码来自 here ,这只是此代码的副本。但我正在尝试从该服务发出一些 HTTP 请求。对于这项工作,我选择创建一个新线程,并在线程内执行
在线程内,我将事件排队以在主线程的上下文中运行,如下所示: TThread.Queue(nil, procedure begin AddDataToChart(SomeData) end); 现在,在
窃取 Uwe Raabe 的文章 Synchronize and Queue with Parameters我这样做: if GetCurrentThreadID = MainThreadID the
我有一个类,它是 TThread 的后代。我有一些只读的公共(public)属性。如果我的主线程在线程处于事件状态时读取这些值,我会遇到问题吗? 最佳答案 如果“只读属性”意味着 TThread 后代
我知道您需要同步(您的程序)来设置例如标签的文本。但是呢: 读取标签的文本。 切换/设置标签的启用属性。 调用其他标签过程/函数(例如 onclick 事件)。 当我需要使用synchronize时,
例如,来自 CreateTimerQueueTimer 回调提供的线程在可执行文件或dll中?与主线程具有相同的线程 ID 很重要。 procedure TMyMainClass.ExecuteMe(
我创建了一个派生自 TThread 的类,因为我希望执行一些异步操作,但是为了避免创建另一个类,我围绕该线程类构建了整个内容。不确定这是否是一个好的做法,如果我不能让它工作,那么我想我别无选择,只能重
我试图在两个独立的项目之间发送消息,但我的问题是我试图让接收器在 TThread 对象内部运行,但 WndProc 无法在对象内部工作,必须是一个函数,无论如何都可以创建TThread 内的窗口可以处
我编写了一个应用程序(使用 Delphi 2009),允许用户选择一系列可以在多个不同系统上运行的查询。为了允许查询同时运行,每个查询都使用 TADOQuery 对象在自己的线程中运行。这一切都运行良
创建一个线程(TThreadStarter),然后它将创建许多工作线程,并且必须等待它们完成才能继续。它必须在 Linux 和 Windows 下都能工作。 下面的方法似乎是一个解决方案。但这是一件好
我是一名优秀的程序员,十分优秀!