默认情况下,Python的运行时在一个由其全局解释器锁(GIL)进行流量控制的线程中执行。 在大多数情况下,这并不是一个很大的瓶颈,但是当您要并行运行许多作业时,它就会变成一个瓶颈。
Python提供了两种方法来解决此问题: 线程和多处理 。 每个选项都允许您执行长期运行的工作,将它们分成并行批处理,然后并行进行。
根据所涉及的工作,有时可以极大地加快操作速度。 至少,您可以以这样一种方式对待任务,即它们在等待完成时不会阻塞其他工作。
在本文中,我们将探讨在Python中使用线程和子进程的最快方法之一,即线程和进程池。
Python 线程是彼此独立运行的工作单元。 但是,它们与CPU上的硬件线程不对应-至少在CPython中不对应。 Python线程由GIL控制,因此它们可以串行运行。 因为一次只能运行一个Python线程,所以它们是组织涉及一些等待的任务的有用方法。 例如,当线程B等待来自外部系统的答复时,Python可以执行线程A或线程C。
Python 进程是独立运行的Python解释器的整个实例。 每个Python进程都有自己的GIL和要处理的数据副本。 这意味着多个Python进程可以在单独的硬件内核上并行运行。 代价是,Python进程的启动时间要比Python线程的启动时间长。
在Python线程和Python进程之间进行选择的方法如下:
使用Python线程和Python进程处理多种作业的最简单方法是使用Python的Pool
对象。 Pool
使您可以定义一组线程或进程(您的选择),您可以提供任意数量的作业,这些作业将按完成顺序返回结果。
举例来说,让我们从1到100的数字列表,从中构造URL,然后并行获取它们。 这个示例是受I / O约束的,因此线程或进程的使用之间可能没有明显的性能差异,但是基本思想应该很清楚。
# Python 3.5+
from multiprocessing.dummy import Pool as ThreadPool from multiprocessing import Pool as ProcessPool from urllib.request import urlopen def run_tasks(function, args, pool, chunk_size=None): results = pool.map(function, args, chunk_size) return results def work(n): with urlopen("https://www.google.com/#{n}") as f: contents = f.read(32) return contents if __name__ == '__main__': numbers = [x for x in range(1,100)] # Run the task using a thread pool t_p = ThreadPool() result = run_tasks(work, numbers, t_p) print (result) t_p.close() # Run the task using a process pool p_p = ProcessPool() result = run_tasks(work, numbers, p_p) print (result) p_p.close()
以上示例的工作方式如下:
multiprocessing
模块为线程( multiprocessing.dummy
)和进程( multiprocessing
)提供池对象。 关于使用multiprocessing
一件好事是拥有相同的线程和子流程API,因此您可以创建可以与两者互换使用的函数,如下所示。
t_p
和p_p
是实例ThreadPool
和ProcessPool
。 两者都作为用于任务的池类型传递给run_tasks
。 默认情况下,每个池实例在每个可用CPU核心上使用一个线程或进程。 创建池有一定数量的开销,因此不要过度使用。 如果您要长时间处理大量作业,请首先创建该池,直到完成后再对其进行处理。 您可以通过调用.close()
函数来处置池。
pool.map()
是我们用来细分作品的函数。 pool.map()
接受一个带有参数列表的函数以应用于该函数的每个实例,将工作拆分为多个块(您可以指定块大小;默认情况下通常很好),然后将每个块馈入工作线程或过程。
通常, map
阻塞正在运行的线程,这意味着在map
返回完成的工作之前,您无法执行其他任何操作。 如果要异步运行map
,请提供一个在其所有作业完成时运行的回调函数,请使用map_async
。
最后,该基本示例仅涉及具有各自状态的线程和进程。 如果您有一个长期运行的CPU绑定操作,其中线程或进程需要彼此共享信息,请考虑对共享内存或服务器进程使用多处理 。
总的来说,您可以对处理和要处理的数据进行分区的越多,所有内容运行的速度就越快。 无论您使用哪种语言,这都是多处理和多线程的基本规则。
From: https://www.infoworld.com/article/3315121/python-threading-and-subprocesses-explained.html