PyHook中单个回调添加延迟导致其他回调无法触发的原因是什么
import pyWinhook as pyHook import time def OnMouseEventLeft(event): print('MessageName: %s' % event.MessageName) time.sleep(2) # 故意添加的延迟 print("------") return True def OnKeyboardEvent(event): print('Key: %s' % event.Key) print('---') return True hm = pyHook.HookManager() hm.MouseLeftDown = OnMouseEventLeft hm.KeyDown = OnKeyboardEvent hm.HookMouse() hm.HookKeyboard() if __name__ == '__main__': import pythoncom pythoncom.PumpMessages()
现象原因
- 该现象由Windows低级钩子的实现机制和Python单线程消息循环的特性共同导致:
- pyHook底层调用的是Windows系统的
LowLevelMouseHook和LowLevelKeyboardHook低级钩子,所有钩子事件都会被系统投递到注册钩子的线程的消息队列中,等待线程的消息循环分发处理。 - 你的程序全程只有一个主线程运行
pythoncom.PumpMessages()消息循环,所有鼠标、键盘的回调函数都在这个单线程内串行执行,不存在独立的鼠标、键盘回调链。 - 当鼠标左键事件触发回调后,
time.sleep(2)会直接挂起整个主线程,消息循环也会完全停止工作。此时你输入的键盘事件会被暂存到系统消息队列里,不会被分发到对应的键盘回调函数,直到2秒延迟结束、鼠标回调返回后,消息循环才会继续处理积压的所有事件。
- pyHook底层调用的是Windows系统的
- 文档里提到的“回调需要尽快返回”不仅仅是影响当前回调链,本质是避免阻塞整个线程的消息调度,所有依赖该消息循环的事件都会被阻塞。
是否为预期行为
属于完全预期的行为。不仅会影响同进程的其他钩子回调,阻塞时间过长的话,Windows系统还会判定你的钩子程序无响应,自动将你的钩子从系统全局钩子链中移除,导致后续所有事件都无法触发回调。
优化方案
如果需要在回调中执行耗时操作,不要直接在回调函数内处理,可通过队列将事件数据转发到独立的工作线程处理,回调函数直接返回,避免阻塞消息循环。
内容的提问来源于stack exchange,提问作者Laishram Krishnananda Singh
相关产品推荐
相关产品推荐

