什么是 TExternalThread? “无法终止外部创建的线程”终止基于线程的计时器时
当关闭我的应用程序时,有一半的时间会发生这种情况,我在设计时将 TLMDHiTimer 放置在窗体上,并将“启用”设置为 true。 在我的 OnFormClose 事件中,我调用 MyLMDHiTimer.Enabled := false。当调用它时,我有时(大约一半的时间)会遇到此异常。
我调试并进入调用,发现 LMDTimer.pas 中的第 246 行给出了此错误。
FThread.Terminate;
我正在使用最新版本的 LMDTools。我在周末之前完全重新安装了 LMD 工具,并已正确删除该组件并将其重新添加到表单中。
据我所知,这与 TExternalThread 有关,但 Embarcadero 没有关于它的文档,而且我没有在 LMDTools 源代码中找到任何引用它的内容。
使用完全更新的 RAD Studio 2010、Delphi 2010。
真正让我感到不安的是没有任何文档。 Google 给出了一个实际讨论该问题的结果,其中有人说该错误是由于尝试终止 TExternalThread 而引起的。 但是看看这个 LMDHiTimer 的源代码,它除了创建一个常规的 TThread 之外,没有一次旨在做任何事情。 我能找到的一个谷歌结果,线程:无法终止外部创建的线程? Embarcadero 上的 提到使用 GetCurrentThread() 和 GetCurrentThreadId() 来获取挂接到现有线程所需的数据,但 TLMDHiTimer 没有这样做。它只是用自己的 Create() 构造函数创建自己的 TThread 后代(当然是重写的,并在构造函数的开头调用继承)
所以...这个 TExternalThread 到底是什么?还有其他人遇到过这种异常吗?也许找到了解决方案或解决方法? 我已经向 LMDTools 自己的支持人员提出了几乎完全相同的问题,但在多个地方询问也没什么坏处。
预先感谢您提供的任何帮助。
This happens half of the time when closing my application in which I have placed a TLMDHiTimer on my form in design time, Enabled set to true.
In my OnFormClose event, I call MyLMDHiTimer.Enabled := false. When this is called, I sometimes (about half of the time) get this exception.
I debugged and stepped into the call and found that it is line 246 in LMDTimer.pas that gives this error.
FThread.Terminate;
I am using the latest version of LMDTools. I did a complete reinstall of LMD tools before the weekend and have removed and re-added the component to the form properly as well.
From what I've found, this has something to do with TExternalThread, but there's no documentation on it from Embarcadero and I haven't found anything referencing it within the LMDTools source code.
Using fully updated RAD Studio 2010, Delphi 2010.
What really upsets me here is that there's no documentation whatsoever. Google yeilds one result that actually talks about it, in which someone says that the error is caused by trying to terminate a TExternalThread.
But looking at the source-code for this LMDHiTimer, not once does it aim to do anything but create a regular TThread.
The one google result I could find, Thread: Cannot terminate an externally created thread? on Embarcadero mentions using GetCurrentThread() and GetCurrentThreadId() to get the data necessary to hook on to an existing thread, but the TLMDHiTimer does no such thing. It just creates its own TThread descendant with its own Create() constructor (overridden of course, and calls inherited at the start of the constructor)
So... What the heck is this TExternalThread? Has anyone else run into this kind of exception? And perhaps figured out a solution or workaround?
I've asked almost the exact same question to LMDTools' own support, but it can't hurt to ask in multiple places.
Thank you in advance for any assistance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
TExternalThread 包装了 Delphi RTL 未创建的线程。它可能代表属于操作系统线程池的线程,也可能代表程序中另一个 DLL 创建的线程。由于线程正在执行不属于关联的 TExternalThread 类的代码,因此 Terminate 方法无法通知线程您希望其停止。
Delphi TThread 对象会将其 Termminate 属性设置为 True,并且被重写的 Execute 方法将定期检查该属性,但由于该线程是非 Delphi 代码,因此没有 Execute 方法,并且任何 Termminate 属性都只会出现在线程代码已经在其他地方编写(不是通过覆盖 Execute)而存在。
新闻组线程表明您的情况可能发生的情况:
这可能是由于组件库中的错误,或者可能是由于您自己的代码中的错误。您可以使用调试器的数据断点来尝试找出答案。在计时器线程的构造函数中设置断点。当程序在那里暂停时,使用“运行”菜单上的“添加断点”命令,使用新对象的 FExternalThread 字段的地址添加数据断点。此后,如果该字段的值发生更改,调试器将暂停并向您显示更改的原因。 (每次运行程序时数据断点都会重置,因为 IDE 假定对象不会每次都分配在同一地址。)
TExternalThread wraps a thread that the Delphi RTL didn't create. It might represent a thread belonging to the OS thread pool, or maybe a thread created by another DLL in your program. Since the thread is executing code that doesn't belong to the associated TExternalThread class, the Terminate method has no way to notify the thread that you want it to stop.
A Delphi TThread object would set its Terminated property to True, and the Execute method that got overridden would be expected to check that property periodically, but since this thread is non-Delphi code, there is no Execute method, and any Terminated property only came into existence after the thread code was already written someplace else (not by overriding Execute).
The newsgroup thread suggests what's probably happening in your case:
It might be due to a bug in the component library, or it could be due to a bug in your own code. You can use the debugger's data breakpoints to try to find out. Set a breakpoint in the timer's thread's constructor. When your program pauses there, use the "Add Breakpoint" command on the Run menu to add a data breakpoint using the address of the new object's FExternalThread field. Thereafter, if that field's value changes, the debugger will pause and show you what changed it. (The data breakpoint will get reset each time you run the program because the IDE assumes the object won't get allocated at the same address each time.)
代码是否有可能尝试终止已销毁的 TThread?如果您设置了 FreeOnTerminate,则很容易发生这种情况。
我在诊断放置在主窗体上的组件的构造函数中的类似(相反?)错误“无法在正在运行或挂起的线程上调用 Start”时注意到您的帖子。当我删除 Start() 时,该错误被相应析构函数中更明显的错误所取代,例如“无效的指针操作”和“访问冲突”。在 TThread 被释放后,该组件试图操纵它的 TThread 对象,从而让事情服从墨菲定律。当我解决这个问题时,我能够替换 Start() 调用,而不会返回“无法调用 Start”错误。
通过类比,您的问题是否可能是 FExternalThread 的地址在析构函数/终止调用之前已被回收并破坏?在我们的例子中,我们的单例实例模式实现有缺陷;但同样,FreeOnTerminate 似乎也可能是一个嫌疑对象。
[仅供参考:我正在使用 RAD Studio XE 下的 C++]
Is there any chance the code might be trying to Terminate an already Destroyed TThread? This could easily happen if you have FreeOnTerminate set.
I noticed your post while diagnosing a similar (opposite?) error, "Cannot call Start on a running or suspended thread" in the constructor of a component placed on the main form. When I removed the Start(), that error was replaced by more telling errors, e.g. "invalid pointer operation" and "access violation", in the corresponding destructor. The component was trying to manipulate its TThread object after the TThread was Freed, thus leaving things up to Murphy's law. When I fixed that, I was able to replace the Start() call without the "Cannot call Start" error returning.
By analogy, could your problem be that the address of your FExternalThread had been recycled and clobbered before the destructor/Terminate call? In our case, we had a buggy implementation of the Singleton Instance Pattern; but again, FreeOnTerminate also seems like a likely suspect.
[FYI: I'm using I'm using C++ under RAD Studio XE]