Python同一socket多线程仅分读写(一写一读)实现是否存在问题?
单写单读双线程操作同一Python Socket的实际风险判断
首先给明确结论:在你描述的架构下——也就是一个socket实例只配一个写线程、一个读线程,没有其他线程会碰这个socket——这种实现没有本质问题,Python socket的线程安全缺陷根本不会触发实际影响。
先厘清Python Socket“线程不安全”的实际含义
很多开发者对socket的线程安全性有误解,以为“只要多个线程碰同一个socket实例就会出问题”,实际上Python socket的非线程安全,特指以下几类会触发竞态的场景:
- 多个线程同时执行写操作:比如两个线程同时调用
send()/sendall(),会因为没有内置的锁保护,导致两个线程的发送内容在用户态缓冲区交叉拼接,最终发出去的TCP流内容顺序错乱 - 多个线程同时执行读操作:多个线程同时调用
recv()时,会竞争读取socket的内核接收缓冲区,每个线程拿到的都是被切分的零散数据块,无法按预期解析业务逻辑 - 读/写操作正在执行时,其他线程同时修改socket的状态:比如正在阻塞等待recv返回时,另一个线程调用
close()、settimeout()、setsockopt()修改socket配置,会触发对象内部状态的竞态,轻则抛连接错误,重则出现文件描述符泄漏
为啥这种场景下不会触发线程安全问题
你搭的这套线程模型,刚好把上面所有会触发竞态的场景都绕开了:
- 写操作全由线程1独占:没有其他写线程跟它抢发送缓冲区,根本不会出现两个线程同时写导致内容交叉错乱的问题
- 读操作全由线程2独占:没有其他读线程跟它抢内核里的接收缓冲区,每次
recv()拿到的都是按TCP流顺序排好的分段,业务解析逻辑不会被打乱 - 没有第三方线程过来改socket的状态:不会出现正阻塞在读/写上呢,突然被其他线程改超时、强行关连接的竞态
从操作系统底层说,每个TCP socket本来就有两个完全独立的内核缓冲区,一个管发送、一个管接收,两个缓冲区的操作互不干扰,内核天生就支持一个线程读、一个线程写的并发访问,根本不存在底层的资源竞争。
几个需要额外注意的边界情况
虽然核心流程没有问题,实际开发中还是有几个容易踩的坑需要规避:
- 不要绕过两个读写线程直接关闭socket:如果业务需要断开连接,建议先给两个线程发退出信号,等两个线程完全退出读写循环后,再执行
close()操作。不然很容易出现正在阻塞的recv()/send()碰到已关闭的文件描述符,直接抛出OSError - 不要跨线程直接修改socket配置:如果需要调整socket的超时时间、阻塞模式等参数,不要直接在主线程或者其他非读写线程调用
settimeout()这类接口,最好把修改配置的请求通过队列传给对应的读写线程,由线程自己执行修改,避免和正在阻塞的读写操作产生竞态 - 不要混用不同的读接口:如果用了
socket.makefile()生成带用户态缓存的类文件对象,就统一用这个对象做所有读操作,不要同时在另一个线程调用原生的socket.recv(),不然会出现带缓存的接口截留部分数据,导致原生读接口拿不到完整内容的问题——这个不属于线程安全问题,是接口使用方式错误,统一读接口即可规避。
内容的提问来源于stack exchange,提问作者haiyu
相关产品推荐
相关产品推荐

