Detecting secondary thread context in Delphi
在 Delphi 2009 和 Windows API 中,有没有办法检测特定代码段是否在辅助线程的上下文中运行?在伪代码中,我想说:
1
2 3 4 5 6 7 |
procedure DoSomething;
begin if InvokedBySecondaryThread then DoIt_ThreadSafeWay else DoIt_RegularWay; end; |
这是一个我编写并使用多年的日志库,现在我正在尝试适应可以从多个线程调用一个过程的情况。我的”常规方式”不是线程安全的。我知道如何使它成为线程安全的,但我只想在实际需要时才使用线程安全方法。
解释(非必读:-)
它归结为使用 SendMessage 和 PostMessage 将记录的消息发送到多个接收器之间的选择,例如日志文件、控制台或 VCL 控件。使用 PostMessage 意味着在长时间的阻塞操作正在进行时将不会收到消息,这在一定程度上违背了记录的目的,尤其是。用于指示进度时。我想我可以用一个关键部分来保护 SendMessage 调用,但我更愿意只在真正需要时才这样做。
我知道 system.pas 中的全局变量 IsMultiThread,但这只会告诉我应用程序已启动辅助线程。这些可能是由 3rd 方库创建的线程,因此它们永远不会访问”我的”代码,因此它们的存在不会影响我的日志记录逻辑。
我真的希望我可以使用相同的低级库代码,无论它是从一个线程还是多个线程调用。例如,从辅助线程内部调用修改后的线程安全日志记录过程会很容易,但这会重复很多代码,而且我仍然必须记住始终做正确的事情。
@Lieven:目前,日志记录逻辑是这样的,有点简化
我希望日志记录尽可能轻松,只需最少的设置代码并且不用担心管理对象的生命周期,因此该库只公开了一些重载的帮助程序,例如
1
2 3 |
procedure Log( const msgText : string; level : TLogLevel = lvNotice ); overload;
procedure Log( const msgText : string; Args : array of const; level : TLogLevel = lvNotice ); overload; etc, including specialized routines that log a StringList, a boolean, an Exception and so on |
几乎所有其他事情都发生在单元的实现中。所有的辅助例程最终都会调用
1
|
procedure _LogPostMessage( const msgText : string; level : TLogLevel );
|
which (a) 检查单例调度程序对象是否已初始化; (b) 创建 TLogMessagePacket 对象的实例(消息文本、时间戳等的容器),最后 (c) 执行 SendMessage 或 PostMessage 以将”数据包”发送到调度程序(它有一个窗口句柄收到)。
然后是一组从抽象 TLogReceiver 类派生的类。一个这样的类接收记录的消息并将它们写入文件,另一个更新 TMemo 等。我实例化我想在项目中使用的具体接收器,并将它们注册到调度程序,调度程序从那时起拥有它们。
当调度程序收到消息”数据包”时,它会依次将它交给每个接收者,然后释放它。
所以我可能固定在上述思维方式中,这就是为什么当你说将调度程序对象交给日志库的代码应该根据线程上下文选择一个时,我不太明白你的想法。调度器确实是库的主要引擎,一次只存在一个。
- 我认为你没有抓住重点。如果您的例程在多线程应用程序的主线程中调用,并且您选择运行非线程安全代码,则它有可能产生冲突(以它不是线程安全的方式)。我想你想知道的是调用例程的应用程序是否是多线程的。
1
2 3 4 5 6 |
if GetCurrentThreadID = MainThreadID then begin
// in main thread end else begin // in secondary thread end; |
- 1 但 OP 应该考虑将此类结构排除在他的日志库之外。 IMO,应该为库提供一个分发消息的对象。将调度程序对象传递给日志库的代码应根据线程上下文选择一个。
- @Lieven:我不确定我是否正确地想象您提出的建议,但我对更好的解决方案感兴趣。我将向 OP 添加对当前日志记录流程的快速说明。
- 在(尝试)进行 TDD 时,我可能会因为过于强调”我将如何测试这个,让我们使用 DI”而感到内疚,并且忘记了所有关于 KISS 的事情。您的编辑清楚地表明,使用(两个)单独的对象执行实际的 Post/SendMessage 几乎没有增加价值。
应您的要求,我添加了一些示例代码来说明我的意思。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
interface
procedure Log( const msgText : string; level : TLogLevel = lvNotice; dispatcher: IDispatcher = nil); overload; … implementation procedure Log( const msgText : string; level : TLogLevel = lvNotice; dispatcher: IDispatcher = nil); overload; dp.msgText := msgText; procedure _LogPostMessage( dispatcher: IDispatcher ); |
现在这给你带来了什么?
除了已经指出的增加了复杂性之外,并没有太多,但它使_LogPostMessage 的测试更容易。
这样看:你将如何编写单元测试来验证消息确实被发布了?
来源:https://www.codenong.com/4097742/