在 Windows Server 上挂了一夜,第二天托盘图标双击点不开、右键也没反应,想退出都退不了。刚启动的时候一切正常。
这是托盘的消息循环没绑在固定的 OS 线程上导致的。
systray 在 Windows 上,是靠创建托盘窗口的那个 OS 线程跑 GetMessageW 收消息的,窗口消息也只会投递到那个线程的队列。而 ProcHub 是把托盘丢到一个新 goroutine 里启动,没有 LockOSThread,Go 调度器跑一段时间(长时间空跑、GC 或调度有压力时更容易)会把这个 goroutine 挪到别的 OS 线程,这时 GetMessageW 抽的是新线程的空队列,窗口消息永远取不到,WndProc 也不再触发 —— 双击、右键就都没反应了,explorer 重启后的 TaskbarCreated 也收不到,图标变成幽灵图标。退出只能走托盘右键菜单,所以连正常退出都不行。
修复是在 Windows 分支的 Run() 里加了 runtime.LockOSThread(),把消息循环固定在拥有窗口的那个线程上;同时把托盘左键的回调放到独立 goroutine 里执行,避免唤起窗口时把消息循环阻塞住。
「隔夜才出现」也正好对得上:线程迁移是概率性的,刚启动那会儿大概率还在原线程上,所以一开始都正常。
QQ扫一扫联系
点击联系
2374926113
微信扫一扫联系
这是托盘的消息循环没绑在固定的 OS 线程上导致的。
systray 在 Windows 上,是靠创建托盘窗口的那个 OS 线程跑 GetMessageW 收消息的,窗口消息也只会投递到那个线程的队列。而 ProcHub 是把托盘丢到一个新 goroutine 里启动,没有 LockOSThread,Go 调度器跑一段时间(长时间空跑、GC 或调度有压力时更容易)会把这个 goroutine 挪到别的 OS 线程,这时 GetMessageW 抽的是新线程的空队列,窗口消息永远取不到,WndProc 也不再触发 —— 双击、右键就都没反应了,explorer 重启后的 TaskbarCreated 也收不到,图标变成幽灵图标。退出只能走托盘右键菜单,所以连正常退出都不行。
修复是在 Windows 分支的 Run() 里加了 runtime.LockOSThread(),把消息循环固定在拥有窗口的那个线程上;同时把托盘左键的回调放到独立 goroutine 里执行,避免唤起窗口时把消息循环阻塞住。
「隔夜才出现」也正好对得上:线程迁移是概率性的,刚启动那会儿大概率还在原线程上,所以一开始都正常。