关于我
编程界的一名小程序猿,目前在一个创业团队任team lead,技术栈涉及Android、Python、Java和Go,这个也是我们团队的主要技术栈。 联系:hylinux1024@gmail.com

0x00 使用进程实现并发

上一篇文章介绍了线程的使用。然而Python中由于Global Interpreter Lock(全局解释锁GIL)的存在,每个线程在在执行时需要获取到这个GIL,在同一时刻中只有一个线程得到解释锁的执行,Python中的线程并没有真正意义上的并发执行,多线程的执行效率也不一定比单线程的效率更高。 如果要充分利用现代多核CPU的并发能力,就要使用multipleprocessing模块了。

0x01 multipleprocessing

与使用线程的threading模块类似,multipleprocessing模块提供许多高级API。最常见的是Pool对象了,使用它的接口能很方便地写出并发执行的代码。

from multiprocessing import Pooldef f(x):return x * xif __name__ == '__main__':with Pool(5) as p:# map方法的作用是将f()方法并发地映射到列表中的每个元素print(p.map(f, [1, 2, 3]))# 执行结果
# [1, 4, 9]
复制代码

关于Pool下文中还会提到,这里我们先来看Process

Process

要创建一个进程可以使用Process类,使用start()方法启动进程。

from multiprocessing import Process
import osdef echo(text):# 父进程IDprint("Process Parent ID : ", os.getppid())# 进程IDprint("Process PID : ", os.getpid())print('echo : ', text)if __name__ == '__main__':p = Process(target=echo, args=('hello process',))p.start()p.join()# 执行结果
# Process Parent ID :  27382
# Process PID :  27383
# echo :  hello process
复制代码
进程池

正如开篇提到的multiprocessing模块提供了Pool类可以很方便地实现一些简单多进程场景。 它主要有以下接口

  • apply(func[, args[, kwds]])
    执行func(args,kwds)方法,在方法结束返回前会阻塞。
  • apply_async(func[, args[, kwds[, callback[, error_callback]]]])
    异步执行func(args,kwds),会立即返回一个result对象,如果指定了callback参数,结果会通过回调方法返回,还可以指定执行出错的回调方法error_callback()
  • map(func, iterable[, chunksize])
    类似内置函数map(),可以并发执行func,是同步方法
  • map_async(func, iterable[, chunksize[, callback[, error_callback]]])
    异步版本的map
  • close()
    关闭进程池。当池中的所有工作进程都执行完毕时,进程会退出。
  • terminate()
    终止进程池
  • join()
    等待工作进程执行完,必需先调用close()或者terminate()
from multiprocessing import Pooldef f(x):return x * xif __name__ == '__main__':with Pool(5) as p:# map方法的作用是将f()方法并发地映射到列表中的每个元素a = p.map(f, [1, 2, 3])print(a)# 异步执行mapb = p.map_async(f, [3, 5, 7, 11])# b 是一个result对象,代表方法的执行结果print(b)# 为了拿到结果,使用join方法等待池中工作进程退出p.close()# 调用join方法前,需先执行close或terminate方法p.join()# 获取执行结果print(b.get())# 执行结果
# [1, 4, 9]
# <multiprocessing.pool.MapResult object at 0x10631b710>
# [9, 25, 49, 121]
复制代码

map_async()apply_async()执行后会返回一个class multiprocessing.pool.AsyncResult对象,通过它的get()可以获取到执行结果,ready()可以判断AsyncResult的结果是否准备好。

进程间数据的传输

multiprocessing模块提供了两种方式用于进程间的数据共享:队列(Queue)和管道(Pipe)

Queue是线程安全,也是进程安全的。使用Queue可以实现进程间的数据共享,例如下面的demo中子进程put一个对象,在主进程中就能get到这个对象。 任何可以序列化的对象都可以通过Queue来传输。

from multiprocessing import Process, Queuedef f(q):q.put([42, None, 'hello'])if __name__ == '__main__':# 使用Queue进行数据通信q = Queue()p = Process(target=f, args=(q,))p.start()# 主进程取得子进程中的数据print(q.get())  # prints "[42, None, 'hello']"p.join()# 执行结果
# [42, None, 'hello']
复制代码

Pipe()返回一对通过管道连接的Connection对象。这两个对象可以理解为管道的两端,它们通过send()recv()发送和接收数据。

from multiprocessing import Process, Pipedef write(conn):# 子进程中发送一个对象conn.send([42, None, 'hello'])conn.close()def read(conn):# 在读的进程中通过recv接收对象data = conn.recv()print(data)if __name__ == '__main__':# Pipe()方法返回一对连接对象w_conn, r_conn = Pipe()wp = Process(target=write, args=(w_conn,))rp = Process(target=read, args=(r_conn,))wp.start()rp.start()# 执行结果
# [42, None, 'hello']复制代码

需要注意的是,两个进程不能同时对一个连接对象进行sendrecv操作。

同步

我们知道线程间的同步是通过锁机制来实现的,进程也一样。

from multiprocessing import Process, Lock
import timedef print_with_lock(l, i):l.acquire()try:time.sleep(1)print('hello world', i)finally:l.release()def print_without_lock(i):time.sleep(1)print('hello world', i)if __name__ == '__main__':lock = Lock()# 先执行有锁的for num in range(5):Process(target=print_with_lock, args=(lock, num)).start()# 再执行无锁的# for num in range(5):#     Process(target=print_without_lock, args=(num,)).start()复制代码

有锁的代码将每秒依次打印

hello world 0
hello world 1
hello world 2
hello world 3
hello world 4
复制代码

如果执行无锁的代码,则在我的电脑上执行结果是这样的

hello worldhello world  0
1
hello world 2
hello world 3
hello world 4
复制代码

除了Lock,还包括RLockConditionSemaphoreEvent等进程间的同步原语。其用法也与线程间的同步原语很类似。API使用可以参考文末中引用的文档链接。
在工程中实现进程间的数据共享应当优先使用队列或管道。

0x02 总结

本文对multiprocessing模块中常见的API作了简单的介绍。讲述了ProcessPool的常见用法,同时介绍了进程间的数据方式:队列和管道。最后简单了解了进程间的同步原语。
通过与上篇的对比学习,本文的内容应该是更加容易掌握的。

0x03 引用

  • python-parallel-programmning-cookbook.readthedocs.io
  • docs.python.org/3/library/t…
  • docs.python.org/3.7/library…
  • docs.python.org/3/glossary.…
  • docs.python.org/3/library/c…

转载于:https://juejin.im/post/5cefdc60f265da1bca51c0cf

快速了解Python并发编程的工程实现(下)相关推荐

  1. python并发编程调优_Python并发编程-并发解决方案概述

    Python并发编程-并发解决方案概述 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.并发和并行区别 1>.并行(parallel) 同时做某些事,可以互不干扰的同一个时 ...

  2. 《转载》Python并发编程之线程池/进程池--concurrent.futures模块

    本文转载自 Python并发编程之线程池/进程池--concurrent.futures模块 一.关于concurrent.futures模块 Python标准库为我们提供了threading和mul ...

  3. python并发编程之协程

    python并发编程之协程 1.协程: 单线程实现并发 在应用程序里控制多个任务的切换+保存状态 优点: 应用程序级别速度要远远高于操作系统的切换 缺点: 多个任务一旦有一个阻塞没有切,整个线程都阻塞 ...

  4. 揭秘Python并发编程——协程

    原文链接:https://baijiahao.baidu.com/s?id=1649450510185145678&wfr=spider&for=pc Python并发编程一直是进阶当 ...

  5. python并发编程之semaphore(信号量)_浅谈Python并发编程之进程(守护进程、锁、信号量)...

    前言:本博文是对Python并发编程之进程的知识延伸,主要讲解:守护进程.锁.信号量. 友情链接: 一.守护进程(daemon) 1.1 守护进程概念 首先我们都知道:正常情况下,主进程默认等待子进程 ...

  6. Python并发编程理论篇

    Python并发编程理论篇 前言 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. 很多已经做案例的人,却不知道如何去学习更加高深的知识 ...

  7. python 并发编程 多线程 目录

    线程理论 python 并发编程 多线程 开启线程的两种方式 python 并发编程 多线程与多进程的区别 python 并发编程 多线程 Thread对象的其他属性或方法 python 并发编程 多 ...

  8. python 并发编程 多进程 目录

    python multiprocessing模块 介绍 python 开启进程两种方法 python 并发编程 查看进程的id pid与父进程id ppid python 并发编程 多进程 Proce ...

  9. Python并发编程系列之多进程(multiprocessing)

    1 引言 本篇博文主要对Python中并发编程中的多进程相关内容展开详细介绍,Python进程主要在multiprocessing模块中,本博文以multiprocessing种Process类为中心 ...

最新文章

  1. CSS三角形如何工作?
  2. 关于fullcalendar里显示json格式的events(java)
  3. 更新项目经常使用的Linux命令
  4. MysqlDataTruncation Mysql 数据插入错误
  5. git idea 图形化_Git大全,你所需要的Git资料都在这里
  6. Simple Web Server
  7. 单片机如何用普通电池供电?
  8. Python数组类型——列表(list)
  9. java对类数组进行排序_Java比较器类对数组进行排序
  10. Kotlin入门(28)Application单例化
  11. Flask和WebSocket
  12. 百度区块链 xuperchain 如何剪枝 裁剪区块
  13. (数据结构整理)NJUPT1054
  14. c246芯片组服务器主板,支持Xeon E-2100系列:ASRock 华擎 发布 C246M WS 主板
  15. Presto SQL 时间日期函数
  16. 微信小程序采坑三:输入框设置自动获取焦点后无法自动获取焦点
  17. vue3+Typescript---Composition API(常用部分)学习笔记(二)
  18. java HashMap集合的使用
  19. 数值计算之 最小二乘法(1)最小二乘计算与线性方程
  20. [转载](asp.net大型项目实践)

热门文章

  1. mysql 判断是否已存在及插入表数据 的 简单存储过程
  2. Android Installation error: INSTALL_FAILED_INSUFFICIENT_STORAGE解决办法
  3. ABI(Application Binary Interface)
  4. Java 对象初始化过程
  5. re 模块 分组特别说明
  6. Python中生成一个指定长度的随机字符串实现示例
  7. 上传jar包到Apache Archiva本地仓库
  8. Project Tango 的一些应用
  9. C++ 的多态性与虚函数
  10. Android Prelink实现的源码分析