并发 20 个线程往同一个 socket 写,服务端偶尔收到半条 A 加半条 B,日志里一条报文声明 4096 字节却只有 2048 是完整 JSON。第一次遇到会往复杂处想,其实漏掉的多是个小前提。

lock = threading.Lock()
with lock:
    sock.sendall(frame)

send 不保证一次写完,两个线程各自调用时字节会交叉。内核写缓冲区是共享的,没有按报文分界,靠 GIL 也挡不住这种交叉。这一点先确认清楚,后面就不用反复试。

「socket 多线程共享」相关的日志我一般连着看前后五分钟,单看一条报错经常判断不出是哪一步失败。临时改动用注释写上日期和原因,过两周自己都记不清为什么这么写。

动手前把原文件复制一份带时间戳,出问题立刻换回去,比现场分析快得多。一次只改一处,改完立刻验证,几个改动叠在一起,出问题就说不清是谁导致的。

具体做法是,每个连接配一把写锁;或者改成单写线程加发送队列;发送前把整帧拼好再用 sendall;检查返回值确认真的写完。

改完照着核一遍,加锁后连续发送 50 万条,服务端解析失败 0 条,报文长度全部符合约定。

作者 9823189