我的 Python 多线程代码是否受到全局解释器锁的影响

Is my Python multithreading code affected by the Global Interpreter Lock

我正在尝试使用 ThreadPool 加速我的代码。

输入是一个包含大约 80000 个元素的字典,每个元素都是 25 个元素的列表。我必须通过处理和组合每个列表中的元素,为字典中的每个元素生成一个输出列表。

所有列表都可以独立分析,所以这个设置应该很容易并行化。

这是我用于 pool.map 的基本设置:

from multiprocessing.dummy import Pool as ThreadPool

pool   = ThreadPool(NUM_THREADS)
output = pool.map(thread_compute, iterable_list)
pool.close()
pool.join

我很快意识到方法 1 不会工作,因为全局变量 input_dict,即使它从未被写入操作访问过(因此应该是线程安全的),它受到 GIL 的保护(link 1, link 2) - 尝试从单独的线程中安全访问 Python 对象时全局强制锁定。

以下是 1、2、4 和 16 个线程的执行时间:

   1:  Done, (t=36.6810s).
   2:  Done, (t=46.5550s).
   4:  Done, (t=48.2722s).
   16: Done, (t=48.9660s).

为什么添加线程会导致时间增加?我确信这个问题可以从多线程中获益,并且认为增加线程的开销不能单独对增加负责。

如果你的 do_stuff_function 是 CPU-bound,那么 运行 它在多线程中将无济于事,因为 GIL 一次只允许执行 1 个线程。

在Python中解决这个问题的方法是使用多进程,只需替换

from multiprocessing.dummy import Pool

from multiprocessing import Pool

尝试使用 Pool (docs),因为它使用 processes 而不是 threads

  • Python 中的线程是 concurrent 而不是 parallel 这基本上意味着解释器将在执行多个线程和同时执行一个线程之间快速切换,为所有其他线程锁定解释器,使印象 运行 并行操作。

  • 另一方面,进程的生成成本要高得多,因为它们基本上是解释器自己的实例,并且它们操作的数据必须序列化,并从主线程发送到工作进程。在那里它被反序列化、计算、结果被序列化并再次发回。

但是,考虑到您发布的中等处理时间,生成进程所需的时间可能会对整体性能产生负面影响。