如何在Python线程中无锁读取只读共享变量?
问题分析与解决方案
嘿,你的问题核心其实不是只读变量被自动加锁,而是CPython的**全局解释器锁(GIL)**在搞鬼!我来给你拆解清楚:
为什么线程没法并行?
CPython里的GIL是一把全局锁,它保证同一时刻只有一个线程能执行Python字节码——不管你是不是只读操作,只要是在执行Python层面的循环、逻辑,GIL就会限制线程的并行性。对于CPU密集型任务(比如你代码里的循环计算),线程其实是交替执行,而不是真正并行,这就会让你产生“好像被加锁了”的错觉。
你的task_A和task_B都是纯CPU计算的循环,GIL会在每个线程执行一定字节码后切换,但整体没法利用多核CPU,速度上甚至可能比单线程还慢(因为线程切换有开销)。
怎么实现真正的并行?
根据你的场景,有两种可行方案:
1. 用multiprocessing代替threading(推荐CPU密集型场景)
每个进程拥有独立的Python解释器和GIL,所以能真正利用多核CPU并行执行。修改你的代码示例:
import multiprocessing import numpy as np def task_B(inc, array): # 进程间数据不共享,需要把array作为参数传入 for elem in array: value = elem + inc if __name__ == '__main__': array = np.random.rand(10) # 创建进程,args要传元组,注意末尾的逗号 p1 = multiprocessing.Process(target=task_B, args=(1, array)) p1.start() # task_A array_copy = list() for elem in array: array_copy.append(elem) p1.join()
⚠️ 注意:进程间的数据是隔离的,所以需要把array作为参数传入子进程,而不是直接共享全局变量。如果需要共享大数组,可以用multiprocessing.Array或者numpy的共享内存方案,避免数据拷贝开销。
2. 利用能释放GIL的操作(适合必须用线程的场景)
如果一定要用线程,那得让任务中的操作能主动释放GIL。比如:
- IO密集型操作:比如
time.sleep()、文件读写、网络请求,这些时候线程会释放GIL,其他线程可以运行。 - numpy矢量化操作:numpy的很多底层实现是C语言写的,执行时会释放GIL,所以把循环改成numpy的矢量化计算,就能让线程并行:
import threading import numpy as np def task_B(inc): # 用numpy矢量化代替Python循环,会释放GIL value = array + inc if __name__ == '__main__': array = np.random.rand(10) # 注意args要写成元组,加个逗号!你的原代码这里有bug t1 = threading.Thread(target=task_B, args=(1,)) t1.start() # task_A也改成numpy操作,同样释放GIL array_copy = array.tolist() t1.join()
这样两个线程的numpy操作会在C层面并行执行,因为GIL被释放了。
顺便纠正一个小bug
你原代码里创建线程的args=(1)是错误的——这个写法是整数1,不是元组,线程会把1当成可迭代对象去拆包,导致参数不匹配错误。正确写法应该是args=(1,)(加个逗号表示这是单元素元组)。
内容的提问来源于stack exchange,提问作者SHM
相关产品推荐
相关产品推荐

