Python线程锁使用疑问:为何A无法始终紧跟B输出?
多线程锁问题分析与解决
问题1:为何子线程1的A、B会被其他字符打断?
原因分析
你仅给子线程1的A、B代码块加了锁,但子线程2对全局变量str的修改完全未使用同一把锁做保护。
Python中str += 'X'并非原子操作,实际分为三步:读取当前str值、拼接新字符、将结果赋值回str。当子线程1持有锁执行A、B的修改时,子线程2因无需获取锁,可在任意时机(包括子线程1执行锁内代码的过程中)抢占CPU修改str,导致A、B之间被插入其他字符。
此外代码还有两个潜在问题:
- 全局变量命名
str与Python内置类型冲突,易引发混淆 - 判断
len(str) >= MAX_LEN和截断str = str[:MAX_LEN]的操作未加锁,多线程场景下会出现竞态条件
解决方法
所有对全局变量str的修改操作(包括读取后修改、截断)必须使用同一把锁保护,确保同一时间只有一个线程能修改str。
问题2:修改锁后输出不够随机的原因及解决方法
原因分析
修改后的代码给单个字符修改操作都加了锁,但每个锁块仅执行一次字符修改,释放锁后线程可能因Python GIL调度机制,连续获得CPU执行权,导致出现连续的ABC或DEF序列,无法得到交错的随机输出。
解决方法
若希望线程更随机地交错执行,可在每个锁块执行完成后,主动让线程让出CPU,给其他线程执行机会。最简单的方式是添加短暂休眠触发线程调度:
示例修正代码:
import threading import time # 重命名全局变量,避免与内置类型冲突 result_str = "" MAX_LEN = 70 lock = threading.Lock() def function_sub1(): global result_str while True: with lock: result_str += 'A' print(f"sub 1 - block 1: {result_str}") result_str += 'B' print(f"sub 1 - block 2: {result_str}") # 主动让出CPU,触发线程调度 time.sleep(0.001) with lock: result_str += 'C' print(f"sub 1 - block 3: {result_str}") time.sleep(0.001) with lock: if len(result_str) >= MAX_LEN: result_str = result_str[:MAX_LEN] break def function_sub2(): global result_str while True: with lock: result_str += 'D' print(f"sub 2 - block 1: {result_str}") time.sleep(0.001) with lock: result_str += 'E' print(f"sub 2 - block 2: {result_str}") time.sleep(0.001) with lock: result_str += 'F' print(f"sub 2 - block 3: {result_str}") if len(result_str) >= MAX_LEN: result_str = result_str[:MAX_LEN] break sub_thread1 = threading.Thread(target=function_sub1) sub_thread2 = threading.Thread(target=function_sub2) sub_thread1.start() sub_thread2.start() sub_thread1.join() sub_thread2.join() print("fin:", result_str, "with len:", len(result_str))
若需保持A、B始终绑定(即AB作为不可拆分的整体),同时让C与其他线程操作交错,只需维持A、B在同一个锁块内,配合锁保护所有修改操作+主动休眠,即可得到类似ABDEABCABF...的随机交错结果。
内容的提问来源于stack exchange,提问作者Tershire
相关产品推荐
相关产品推荐

