前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊在Python如何实现并行

聊聊在Python如何实现并行

作者头像
哒呵呵
发布2019-03-01 17:28:31
7730
发布2019-03-01 17:28:31
举报
文章被收录于专栏:鸿的学习笔记鸿的学习笔记

目录

何为并行和并发Python有哪些相关的模块该如何选择合适的模块CPU-bound和I/O-bound问题threading、asyncio和multiprocessing优劣抉择结论

何为并行和并发

在文章开始之前先看看来自 StackOverflow 的一篇回答是如何解释并行和并发的。(https://stackoverflow.com/questions/1050222/what-is-the-difference-between-concurrency-and-parallelism)

Concurrency is when two or more tasks can start, run, and complete in overlapping time periods. It doesn't necessarily mean they'll ever both be running at the same instant. For example, multitasking on a single-core machine. Parallelism is when tasks literally run at the same time, e.g., on a multicore processor.

对于并发(Concurrency)和并行(Parallelism)的核心差别在于at the same time。对于Python而言,有很多与并发相关的名称,例如thread、task、process,但其核心都一样,简而言之,就是按照一定顺序执行的一系列指令(a sequence of instructions that run in order)。至于为啥有这么多名称,因为在具体执行上会有些细微差别,例如 Async IO 和 Threading 所代表的处理逻辑并不一样。

对于并行而言就比较孤独了,只有 multiprocessing 。 由于Python的GIL(全局解释锁)的存在导致没有向Java等JVM语言上的真正意义上的多线程并行(除了Jython,所以这么一看GIL也不一定是Python这门语言必须的),只能使用 multiprocessing 拷贝上下文的多进程实现真正意义的并行,而Async IO和Threading实际是单核下的多任务调度。

Python有哪些相关的模块

对于 threading 而言,操作系统知道每一个线程的运行情况以及拥有可以在任何时间打断其运行,然后运行其他线程的能力,这就是所谓的pre-emptive multitasking(抢占式多任务)。顾名思义,操作系统可以在任何时间抢占并调度线程。对于抢占式任务,核心问题在于“任何时间”,但这会导致x = x + 1这类的语句产生问题。

而 asyncio 使用事件循环这个Python对象,利用协同式方式处理多任务(cooperative multitasking),任务之间的切换取决于任务是否完成,是否已经准备好被切换。具体可以参考

https://stackoverflow.com/questions/49005651/how-does-asyncio-actually-work/51116910#51116910

至于 multiprocessing ,与 asyncio 和 threading 设计思路完全不一样,multiprocessing 中每一个进程都拥有自己的Python解释器以及上下文信息,因此每一个进程都可以运行在不同的CPU核心上。

三者的简单区别如下:

并发类型

任务选择权

核心数

抢占式多任务(threading)

由操作系统决定任务的执行权

1

协同式多任务(asyncio)

取决于任务的执行情况

1

多核心任务(multiprocessing)

所有进程在同一时间执行任务

很多

该如何选择合适的模块

CPU-bound和I/O-bound问题

在决定如何选择之前,要解决两个主要问题CPU-bound和I/O-bound,因为所有的问题都可以归结为这两类问题。

对于这两个问题,在 StackOverflow 的有一段解释相当易懂,如下:(https://stackoverflow.com/questions/868568/what-do-the-terms-cpu-bound-and-i-o-bound-mean)

A program is CPU bound if it would go faster if the CPU were faster, i.e. it spends the majority of its time simply using the CPU (doing calculations). A program that computes new digits of π will typically be CPU-bound, it's just crunching numbers. A program is I/O bound if it would go faster if the I/O subsystem was faster. Which exact I/O system is meant can vary; I typically associate it with disk, but of course networking or communication in general is common too. A program that looks through a huge file for some data might become I/O bound, since the bottleneck is then the reading of the data from disk (actually, this example is perhaps kind of old-fashioned these days with hundreds of MB/s coming in from SSDs).

因此,对于I/O-bound的问题,程序运行的速度主要取决于外部的输入输出,并且SQLAlchemy的作者有一篇经典文章(https://techspot.zzzeek.org/2015/02/15/asynchronous-python-and-databases)对I/O-bound下的关于外部数据库连接对程序运行的影响进行了详细的讨论。而对于CPU-bound的问题,程序的运行速度取决于CPU运行有多块,例如数学运算等等。

两者的简单对比如下:

I/O-Bound Process

CPU-Bound Process

程序花费了大量在处理如网络连接、磁盘读写等问题。

程序花了大量时间在执行CPU操作

需要在等待时间尽可能做多的事

需要尽可能的做多的CPU操作

threading 、asyncio和multiprocessing优劣

threading 模块的核心是ThreadPoolExecutor ,拆开来看就是Thread + Pool + Executor 。Thread 是Python中专门处理线程的包,Pool是线程池,用于创造线程运行的环境,而Executor则是具体的执行者,控制线程池中的每一个线程如何运行和什么时候运行。对于 threading ,程序需要保证数据的线程安全,例如使用Queue模块,不仅如此,还要处理竞争条件(不同线程对同一资源的争抢)。

asyncio 是Python3才引入的模块, 在Python3.5以后又进一步引入了async和await关键字,3.7简化了运行模式整合进running函数中。其核心是事件循环(event loop),事件循环控制这每一个任务如何运行以及何时运行,也就是说事件循环需要维护一个关于事件状态的列表。对于 asyncio 而言,进一步优化了threading对线程池的调度,但局限于协同式任务的弊病,当一个任务因为某些代码问题导致CPU运行时间过长就会导致其他任务无法运行。

multiprocessing 则突破了单CPU运行的局限,使Python代码可以运行在多CPU环境,受限于GIL(https://realpython.com/python-gil/)的存在,multiprocessing 需要复制Python解释器环境,相对于线程,付出了更为高昂的代价。

抉择

综上所述,对于I/O-bound的问题,如果是Python3.6以上,应该优先选择asyncio,但是考虑到低版本Python的兼容,threading 也应该被考虑进来,而CPU-bound则只能使用 multiprocessing 操控多核进行计算。

结论

对于并发和并行问题,一直是个大问题,正如 Donald Knuth 所言:“Premature optimization is the root of all evil (or at least most of it) in programming.”(过早的优化是万恶之源),使用threading、asyncio和multiprocessing需要对代码做出大量的改进,只有当运行时间带来的影响远远大于修改代码的时间时,才需要考虑引入并行,并小心翼翼的处理并行中所可能会带来的诸如竞争等问题。

参考文献: https://realpython.com/python-concurrency/ https://realpython.com/async-io-python/ https://en.wikipedia.org/wiki/Preemption_%28computing%29#Preemptive_multitasking https://en.wikipedia.org/wiki/Cooperative_multitasking

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 鸿的笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 何为并行和并发
  • Python有哪些相关的模块
  • 该如何选择合适的模块
    • CPU-bound和I/O-bound问题
      • threading 、asyncio和multiprocessing优劣
        • 抉择
        • 结论
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档