Skip to content

增加threadvar的初始化功能。 #191

Description

@hezihang12

目前存在threadvar 的初始化时机不可控。如果需要扩展DelphiCrossSocket增加threadvar变量时,建议worker thread起来后,马上发送一个特殊的io到每个线程,线程处理这个io时,完成threadvar变量的初始化。建立HandleInit等虚方法。

一、当前问题
threadvar在 FPC 中保证零初始化(nil),所以当前代码恰好安全。但如果后续需要在 worker 线程初始化时做更复杂的事情(比如预分配 PerIoData 池、预创建 内存池、注册线程到诊断系统),没有初始化钩子就很麻烦。

threadvar初始化方案

Reactor 启动 worker 线程:
worker.Run()
→ 给自己 PostQueuedCompletionStatus(一个特殊的 "init" IO)
→ ProcessIoEvent 收到 "init" IO
→ _HandleInit: 初始化 threadvar (PerIoData 池预热、内存 池预热等)
→ 正常进入 recv/send 循环
优点:

确定性初始化:不依赖 threadvar 的零初始化保证,显式初始化每个 worker 线程的本地状态
预热:可以预分配 buffer/PerIoData/builder,避免第一批请求时的冷启动分配
诊断:可以记录每个 worker 线程的 ThreadID(替代 OnIoThreadBegin 回调)
可行性:完全可行。Cross-Socket 已有 TriggerIoThreadBegin/TriggerIoThreadEnd 回调(我们之前接入过)。但用 IO 投递更可靠——因为 TriggerIoThreadBegin 在 Execute 的 try 块内调用,此时线程已就绪但可能还没进入 IOCP 循环。

更简单的替代:其实不用投递特殊 IO——直接在 TriggerIoThreadBegin 回调里初始化即可。这个回调在每个 worker 线程启动时、在该线程的上下文中被调用(TIoEventThread.Execute → FOwner.TriggerIoThreadBegin(Self)),此时 threadvar 已经是该线程的副本:

pascal
procedure TMyIocpCrossSocket.OnIoThreadBegin(Sender: TObject; AIoThread: TIoEventThread);
begin
{ 在 worker 线程上下文中调用: 初始化该线程的 threadvar }
SrPioFreeList := nil; { 显式初始化 (虽然 FPC 保证零, 但更安全) }
SrPioFreeCount := 0;
SrLocalCache := nil; { buffer pool 线程缓存 }
{ 预热: 预分配几个 PerIoData 和 buffer }
PoolWarmUp;
end;
对比:

方案 初始化时机 预热 复杂度
TriggerIoThreadBegin 回调 worker 线程启动时 ✅ 可预分配 低(已有回调)
投递 "init" IO worker 线程进入 IOCP 循环后 ✅ 中(需定义特殊 IO 类型)
依赖 threadvar 零初始化 首次访问时(惰性) ❌ 冷启动 最低(当前)
我的建议:用 TriggerIoThreadBegin(已有回调,无需额外 IO 投递),在里面做显式初始化 + 预热。这比投递特殊 IO 更简单,且效果相同。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions