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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 02:49:11