Python中的多个进程和线程

4 投票
3 回答
3203 浏览
提问于 2025-04-18 15:20

我听说过这样一句话:“如果你想让并行应用程序达到最佳性能,你应该创建和你电脑的CPU数量一样多的进程,然后在每个进程中再创建一些(多少个呢?)线程。”

这是真的吗?

我写了一段代码来实现这个想法:

import multiprocessing, threading

number_of_processes = multiprocessing.cpu_count()
number_of_threads_in_process = 25   # some constant


def one_thread():
    # very heavyweight function with lots of CPU/IO/network usage
    do_main_work()


def one_process():
    for _ in range(number_of_threads_in_process):
        t = threading.Thread(target=one_thread, args=())
        t.start()


for _ in range(number_of_processes):
    p = multiprocessing.Process(target=one_process, args=())
    p.start()

这样做对吗?我的 do_main_work 函数真的能并行运行,而不会遇到任何GIL的问题吗?

谢谢。

3 个回答

0

我觉得你每个进程使用的线程数量有点多。一般来说,对于任何英特尔处理器,每个进程的线程数是2。处理器的核心数量从2个(比如英特尔的i3)到6个(比如英特尔的i7)不等。所以当所有进程同时运行时,最多的线程数量就是6个核心乘以2,也就是12个线程。

2
# very heavyweight function with lots of CPU/IO/network usage

很多CPU会因为GIL而受到影响,所以你只有通过多个进程才能获得好处。

IO网络(其实网络也是一种IO)不会受到GIL太大的影响,因为在IO操作完成后,会被明确释放,然后再重新获取。在CPython中有相关的宏定义:

Py_BEGIN_ALLOW_THREADS
... Do some blocking I/O operation ...
Py_END_ALLOW_THREADS

由于在包装代码中使用了GIL,性能还是会受到一些影响,但使用多个线程的性能仍然会更好。

最后——这是一个通用的规则——不仅仅适用于Python:最佳的线程/进程数量取决于程序实际在做什么。一般来说,如果程序对CPU的使用非常密集,当进程数量超过CPU核心数时,几乎不会有性能提升。例如,Gentoo的文档提到,编译器的最佳线程数是CPU核心数加1。

3

这其实很大程度上取决于你在做什么。

要记住,在CPython中,一次只能有一个线程在执行Python字节码(这是因为有个叫GIL的东西)。所以如果你在CPython中处理计算密集型的问题,线程的帮助可能不大。

一种可以让工作并行处理的方法是使用multiprocessing.Pool。默认情况下,这个池不会使用超过你CPU核心数量的进程。如果使用的进程多于CPU核心,反而会让它们争抢资源(比如CPU和内存),这样反而没什么用。

不过,要充分利用多个处理器,你得有足够的工作让它们去做!换句话说,如果问题不能被分解成可以单独计算的小块,并且这些小块可以并行处理,那么很多CPU核心也没什么用。

另外,并不是所有的问题都是计算量大的。

计算机的内存(RAM)比CPU慢得多。如果你处理的数据集比CPU的缓存大得多,从内存中读取数据和返回结果可能会成为速度的瓶颈。这种情况被称为内存限制

如果你处理的数据量远远超过机器的内存,你的程序就会频繁地从硬盘读取和写入数据。相比于内存,硬盘的速度很慢,而相比于CPU,硬盘就更慢了,所以你的程序会变得I/O限制

撰写回答