Python线程和子流程说明

默认情况下,Python的运行时在一个由其全局解释器锁(GIL)进行流量控制的线程中执行。 在大多数情况下,这并不是一个很大的瓶颈,但是当您要并行运行许多作业时,它就会变成一个瓶颈。

Python提供了两种方法来解决此问题: 线程多处理 。 每个选项都允许您执行长期运行的工作,将它们分成并行批处理,然后并行进行。

[InfoWorld的要点: 开始使用Anaconda,这是数据科学的Python发行版 Python的Anaconda发行版中的新增功能 5个用于数据科学的基本Python工具-现已改进 | 通过InfoWorld的App Dev Report新闻通讯了解编程方面的热门话题。 ]

根据所涉及的工作,有时可以极大地加快操作速度。 至少,您可以以这样一种方式对待任务,即它们在等待完成时不会阻塞其他工作。

在本文中,我们将探讨在Python中使用线程和子进程的最快方法之一,即线程进程池。

Python线程与Python进程

Python 线程是彼此独立运行的工作单元。 但是,它们与CPU上的硬件线程不对应-至少在CPython中不对应。 Python线程由GIL控制,因此它们可以串行运行。 因为一次只能运行一个Python线程,所以它们是组织涉及一些等待的任务的有用方法。 例如,当线程B等待来自外部系统的答复时,Python可以执行线程A或线程C。

Python 进程是独立运行的Python解释器的整个实例。 每个Python进程都有自己的GIL和要处理的数据副本。 这意味着多个Python进程可以在单独的硬件内核上并行运行。 代价是,Python进程的启动时间要比Python线程的启动时间长。

在Python线程和Python进程之间进行选择的方法如下:

  • 如果要执行长时间运行的I / O绑定操作 ,则涉及 Python 外部等待服务的任务(例如,多个并行Web抓取或文件处理作业)将使用线程。
  • 如果您正在执行由C编写的外部库 (例如NumPy) 处理的长时间运行的CPU绑定操作 ,请使用线程(因为此处的工作也在Python外部完成)。
  • 如果要在Python中执行长时间运行的CPU绑定操作 ,请使用进程。

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()

Python多处理示例

以上示例的工作方式如下:

multiprocessing模块为线程( multiprocessing.dummy )和进程( multiprocessing )提供池对象。 关于使用multiprocessing一件好事是拥有相同的线程和子流程API,因此您可以创建可以与两者互换使用的函数,如下所示。

t_pp_p是实例ThreadPoolProcessPool 。 两者都作为用于任务的池类型传递给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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值