我可以将多线程应用于python中的计算密集型任务吗?

问题描述

更新:为节省您的时间,我在此处直接给出答案。如果您使用纯Python编写代码,则Python 不能同时使用多个cpu内核。但是Python 可以在调用某些用C编写的函数或程序包时同时使用多核,例如Numpy等。


我听说“由于GIL, Python中的多线程不是真正的多线程”。而且我还听说过“ Python多线程可以处理IO密集型任务,而不是计算密集型任务,因为同时只能运行一个线程”。

但是我的经历让我重新考虑了这个问题。我的经验表明,即使对于计算量很大的任务,python多线程 几乎可以加速计算。 (在使用多线程之前,运行以下程序花了我300秒钟,而在使用多线程之后,我花了100秒钟。)

下图显示python使用cpython作为包threading的编译器创建了5个线程,并且所有cpu cores都占100%的百分比。

我认为屏幕截图可以证明5个cpu内核同时运行。

那么有人可以给我解释吗?我可以将多线程应用于python中的计算密集型任务吗?还是可以在python中同时运行多个线程/内核?

我的代码


import threading
import time
import numpy as np
from scipy import interpolate


number_list = list(range(10))

def image_interpolation():
    while True:
        number = None
        with threading.Lock():
            if len(number_list):
                number = number_list.pop()
        if number is not None:
            # Make a fake image - you can use yours.
            image = np.ones((20000,20000))
            # Make your orig array (skipping the extra dimensions).
            orig = np.random.rand(12800,16000)
            # Make its coordinates; x is horizontal.
            x = np.linspace(0,image.shape[1],orig.shape[1])
            y = np.linspace(0,image.shape[0],orig.shape[0])
            # Make the interpolator function.
            f = interpolate.interp2d(x,y,orig,kind='linear')

        else:
            return 1

workers=5
thd_list = []
t1 = time.time()

for i in range(workers):
    thd = threading.Thread(target=image_interpolation)
    thd.start()
    thd_list.append(thd)

for thd in thd_list:
    thd.join()

t2 = time.time()
print("total time cost with multithreading: " + str(t2-t1))
number_list = list(range(10))

for i in range(10):
    image_interpolation()

t3 = time.time()

print("total time cost without multithreading: " + str(t3-t2))

输出为:

total time cost with multithreading: 112.71922039985657
total time cost without multithreading: 328.45561170578003

top 多线程期间的屏幕截图

top

top -H 多线程期间的屏幕截图

top -H

top的屏幕截图,然后按1过程中

top press 1

top -H 没有多线程的屏幕截图

top

解决方法

正如您所提到的,Python具有一个“全局解释器锁”(GIL),可以防止两个 Python代码线程同时运行。多线程可以加快IO绑定任务的原因是,例如,在侦听网络套接字或等待磁盘读取时,Python会释放GIL。因此,GIL不会阻止您的计算机同时完成两项工作,而是阻止了同一Python进程中的两个Python线程同时运行。

在您的示例中,您使用numpy和scipy。这些主要由C编写,并利用C / Fortran / Assembly编写的库(BLAS,LAPACK等)。在numpy数组上执行操作时,类似于在该the GIL is released中监听套接字。当释放GIL并调用numpy数组操作时,numpy可以决定如何执行工作。如果需要,它可以产生其他线程或进程,并且它调用的BLAS子例程可能会产生其他线程。如果您要从源代码编译numpy,则可以在构建时配置是否/如何完成配置。

因此,总而言之,您已经找到该规则的例外。如果您仅使用纯Python函数重复进行实验,则会得到完全不同的结果(例如,请参见上面链接到页面的“比较”部分)。

,

Python线程是一个真正的线程,只是解释器中一次不能有两个线程(这就是GIL的目的)。该代码的本机部分可以很好地并行运行,而不会在多个线程上争用,只有在返回解释器时,它们才必须在彼此之间进行序列化。

仅将所有CPU内核都加载到100%的事实并不能证明您在“高效”地使用计算机。您需要确保CPU使用率不是由于上下文切换引起的。

如果切换到多处理而不是线程处理(它们非常相似),则不必双重猜测,但是在线程之间传递时,必须封送有效负载。

所以无论如何都要测量一切。