使用ThreadPoolExecutor运行两个共享全局变量的阻塞函数出现死锁,如何修复实现异步交替执行?
问题根因
两个关键错误直接导致了当前的运行结果和伪“死锁”表现:
- 线程池传参方式完全错误,根本没实现并发:你调用
executor.submit()时给函数名加了括号,相当于先在主线程同步执行incA()这个无限循环,等incA()因为检测到q键触发break完全跑完,才会走到下一行代码执行printA(),等printA()也在主线程跑完,才会把两个函数返回的None值丢给线程池。线程池拿到不可执行的None,自然会卡在等待状态,你看到的前面一长串inc日志、最后一行print日志,完全是主线程串行跑两个函数的结果,根本没进入子线程并发执行。 - 无让出的高速循环导致调度完全不可控:就算修正传参问题,两个没有任何暂停的死循环会一直占满CPU,Python的GIL调度会随机让某个线程连续跑成百上千次才切给另一个,根本无法实现稳定的交替输出效果。
分步修复方案
修正submit的传参写法
把函数引用本身传给submit,不要加括号触发主线程同步执行:# 错误写法:会先在主线程直接执行函数,再把返回值传给线程池 # f1 = executor.submit(incA()) # f2 = executor.submit(printA()) # 正确写法:把函数对象传入,由线程池在子线程中调用执行 f1 = executor.submit(incA) f2 = executor.submit(printA)在每个循环中添加微小延迟,主动让出CPU
导入time模块,在两个函数的循环内部加1毫秒左右的暂停,不让单个线程一直占用GIL,给调度器机会交替切换执行权,输出自然会形成自然交替的节奏:import time def incA(): global a while True: print(f'inc a: {a} -> {a+1}') a += 1 time.sleep(0.001) # 主动让出CPU if keyboard.is_pressed('q'): break def printA(): while True: print(f'print a: {a}') time.sleep(0.001) # 主动让出CPU if keyboard.is_pressed('q'): break(可选)严格1:1交替的输出控制
如果需要完全严格的交替顺序(避免调度随机导致的偶尔连续多次输出同一个函数的日志),可以加全局状态标志做顺序控制:# 全局执行权限标志 can_inc = True def incA(): global a, can_inc while True: if can_inc: print(f'inc a: {a} -> {a+1}') a += 1 can_inc = False # 执行完inc,把权限交给print函数 time.sleep(0.001) if keyboard.is_pressed('q'): break def printA(): global a, can_inc while True: if not can_inc: print(f'print a: {a}') can_inc = True # 执行完print,把权限交回inc函数 time.sleep(0.001) if keyboard.is_pressed('q'): break
修复后的运行效果
修正传参、添加CPU让出延迟后,两个函数会在独立子线程并发运行,控制台会按照预期节奏交替输出inc和print的日志,按下q键可以正常触发两个线程退出,不会出现主线程串行运行、程序卡在等待的伪死锁问题。
内容的提问来源于stack exchange,提问作者romhayh
相关产品推荐
相关产品推荐

