Python-多进程,,1、多进程: 由于
Python-多进程,,1、多进程: 由于
1、多进程:
由于Python的GIL,多线程未必是CPU密集型程序的最好选择。
多进程可以完全独立的进程环境中运行程序,可以充分的利用多处理器。
但是进程本身的隔离带来的数据不共享也是一个问题,而且线程比进程轻量级。
2、multiprocessing:
Process类遵循了Thread类的API
测试:开启三个进程,计算一个CPU密集型程序。
1 import multiprocessing 2 import datetime 3 4 # 次函数是一个CPU 密集型,所以,多线程和单线程 分别计算5 次, 计算是一样的效果,至少4分钟左右 5 def calc(i): 6 sum = 0 7 for _ in range(100000000): 8 sum += 1 9 print(i, sum)10 11 if __name__ == ‘__main__‘:12 start = datetime.datetime.now()13 14 ps = []15 for i in range(3):# 启动三个进程。16 p = multiprocessing.Process(target=calc, args=(i,), name=‘cac={}‘.format(i))17 ps.append(p)18 p.start() # 分别启动三个进程。19 for p in ps:20 p.join() # 当前主进程 的主线程等待所有的子进程结束后,再退出。21 22 delta = (datetime.datetime.now() - start).total_seconds()23 print(delta)24 print(‘end---‘)测试:多进程
1 1 1000000002 0 1000000003 2 1000000004 10.2214195 end---
测试:同时CPU 密集型 ,单线程,执行3次:
1 import multiprocessing 2 import datetime 3 import logging 4 5 6 logging.basicConfig(level=logging.INFO, format=‘%(thread)s %(message)s‘) 7 start = datetime.datetime.now() 8 def calc(i): 9 sum = 010 for _ in range(100000000):11 sum += 112 print(i, sum)13 14 calc(0)15 calc(1)16 calc(2)17 18 delta = (datetime.datetime.now() - start).total_seconds()19 logging.info(delta)测试:单线程执行三次
1 0 1000000002 1 1000000003 6088 23.5203464 2 100000000
测试:同上CPU 密集型,多线程,执行;
1 import multiprocessing 2 import datetime 3 import logging 4 import threading 5 # 次函数是一个CPU 密集型,所以,多线程和单线程 分别计算5 次, 计算是一样的效果,至少4分钟左右 6 7 logging.basicConfig(level=logging.INFO, format=‘%(thread)s %(message)s‘) 8 start = datetime.datetime.now() 9 def calc(i):10 sum = 011 for _ in range(100000000):12 sum += 113 print(i, sum)14 15 t1 = threading.Thread(target=calc, args=(1,))16 t2 = threading.Thread(target=calc, args=(2,))17 t3 = threading.Thread(target=calc, args=(3,))18 19 t1.start()20 t2.start()21 t3.start()22 t1.join()23 t2.join()24 t3.join()25 26 27 delta = (datetime.datetime.now() - start).total_seconds()28 logging.info(delta)测试:多线程开启三个子线程
2 1000000003 1000000009908 22.1072651 100000000
总结:
1、可以看到:CPU密集型,多线程和单线程执行的时间是差不多的,也就是说,由于GIL(全局解释器锁,进程锁)的存在,导致,即便是多线程,也是串行执行
2、可以看到,多进程执行,时间要很短,而且充分利用CPU资源,每个进程调度到一个CPU上,但是并没有绑定。但是如果绑定的话,就分别在自己的绑定的CPU上运行,此外CPU绑定,是为了利用cpu缓存。
3、注意的一点,多进程代码一定要在 __name__ == ‘__main__‘ 中执行。
线程是没有如下属性或方法:
名称 | 说明 |
pid | 进程ID |
exitcode | 进程的退出状态 |
terminate() | 终止指定的进程 |
3、进程间同步:(不怎么用,不过注意,方法,类相同,但是来自不同的模块下,线程是线程的,进程是进程的)
Python在进程间同步提供了和线程同步一样的类,使用的方法一样,使用的效果也类似。
不过,进程间代价要高于线程间,而且 底层实现是不同的,只不过Python屏蔽了这些不同之处,让用户简单使用多进程。
multiprocessing 还提供共享内存,服务器进程来共享数据,还提供了用于 进程间通讯的Queue队列,Pipe管道
通信方式不同:
1、多进程就是启动多个解释器进程,进程间通信必须序列化,反序列化
2、数据的线程安全性问题
如果 每个进程中没有实现多线程,GIL 可以说没什么用了
注:
queue模块是给线程用的,但是进程的Queue是multiprocessing提供的,一般不用,是本机使用
进程和进程之间通信:使用第三方Queue,如:kfakfa,而且基本都是网络通信(进程间通讯)
多进程,就是多解释器进程
进程间通讯:一般都要序列话,反序列化。
进程间通讯的锁:分布式锁, zookeper
4、进程池举例:
multiprocessing.Pool 是进程池类
名称 | 说明 |
apply(self,func,args=(),kwds={}) | 阻塞执行,导致主进程执行其他子进程就像一个执行 |
apply_async(self,func, args=(),kwds={}, callback=None, error_callback=None) | 与apply方法用法一致,非阻塞异步执行,得到结果后执行回调 |
cose() | 关闭池,池不能再接受新的任务 |
terminate() | 结束工作进程,不再处理未处理的任务 |
join() | 主进程阻塞等待子进程的退出,join方法要在close或terminate之后使用 |
测试:进程池的使用apply_async
1 import multiprocessing 2 import datetime 3 import logging 4 import threading 5 # 次函数是一个CPU 密集型,所以,多线程和单线程 分别计算5 次, 计算是一样的效果,至少4分钟左右 6 start = datetime.datetime.now() 7 8 def calc(i): 9 sum = 010 for _ in range(100000000):11 sum += 112 print(i, sum)13 14 if __name__ == ‘__main__‘:15 16 17 ps = []18 19 pool = multiprocessing.Pool(3)20 for i in range(3):21 pool.apply_async(calc, args=(i,))22 pool.close()23 pool.join()24 25 delta = (datetime.datetime.now() - start).total_seconds()26 print(delta)27 print(‘end---‘)4核心CPU处理三个进程
2 10000000010.15358end---
总结:
可以看出,利用进程池得出的结果和上面的结果差不多的。
有个前提要注意:
首先测试的电脑是4核心cpu,处理三个进程不会出现不够用,如果处理的进程如果大于CPU 核心数,就会出现等待,
但是这样,利用进程池,可以有效的使用有限资源,剩余的资源可以做别的事,但是,像上面的情况,就会出现明显的资源抢占,资源不够用。
所以,一般推荐使用线程池。
测试:进程池的使用apply,可以看到,一个一个执行完,没有多进程优势存在
1 import multiprocessing 2 import datetime 3 import logging 4 import threading 5 # 次函数是一个CPU 密集型,所以,多线程和单线程 分别计算5 次, 计算是一样的效果,至少4分钟左右 6 start = datetime.datetime.now() 7 8 def calc(i): 9 sum = 010 for _ in range(100000000):11 sum += 112 print(i, sum)13 14 if __name__ == ‘__main__‘:15 16 17 ps = []18 19 pool = multiprocessing.Pool(3)20 for i in range(3):21 pool.apply(calc, args=(i,))22 pool.close()23 pool.join()24 25 delta = (datetime.datetime.now() - start).total_seconds()26 print(delta)27 print(‘end---‘)apply
0 1000000001 1000000002 10000000022.069262end---
测试:回调
1 import multiprocessing 2 import datetime 3 import logging 4 import threading 5 # 次函数是一个CPU 密集型,所以,多线程和单线程 分别计算5 次, 计算是一样的效果,至少4分钟左右 6 logging.basicConfig(level=logging.INFO, format=‘%(process)s %(processName)s %(thread)s %(message)s‘) 7 start = datetime.datetime.now() 8 9 def calc(i):10 sum = 011 for _ in range(100000000):12 sum += 113 return i14 15 if __name__ == ‘__main__‘:16 ps = []17 18 pool = multiprocessing.Pool(3)19 for i in range(3):20 pool.apply_async(calc, args=(i,), callback=lambda x: logging.info(‘x ={}‘.format(x)))21 pool.close()22 pool.join()23 24 delta = (datetime.datetime.now() - start).total_seconds()25 print(delta)26 print(‘end---‘)callback
3848 MainProcess 7764 x =03848 MainProcess 7764 x =23848 MainProcess 7764 x =113.521773end---
回调:自己去主动调用,这里的回调就是,每个任务结束后的返回值,作为回调的参数传入。
而使用多线程,线程的返回值 是不能被用到的。结束就结束了。
5、多进程,多线程的选择:
1、CPU密集型
CPython 中使用到了GIL ,多线程的时候锁相互竞争,且多核优势不能发挥。Python多进程效率更高。
2、IO密集型
适合是用多线程,可以减少多进程间IO 的序列化开销,且在IO等待的视乎,切换大其他线程继续执行,效率不错。
6、应用
请求/ 应答 模型:WEB应用中常见的处理模型
master 启动朵儿worker工作进程,一般 额CPU 数目相同,发挥多核优势。
worker工作进程中,往往需要操作网络IO 和 磁盘 IO ,启动多线程,提高并发处理能力worker处理用户的请求,往往需要等待数据,处理完请求还要通过网络IO 返回响应。
这就是Nginx 工作模式。
worker进程开启多线程,因为要进行IO ,所以多线程相对合适。
apply 是同步阻塞,不推荐使用 apply_async :是异步非阻塞 回调就是调用你的函数。调用空闲函数 自己的任务结束后,才会执行,calback必须有一个参数,接受前面函数的返回值 concurrent : 多进程和多线程,注意是cpu密集型还是IO 密集型logging模块:
Python-多进程
相关内容
- python,,paramiko 模
- python 微服务开发书中几个方便的python框架,python框架
- Python字符串格式化--format()的应用,python--format,1.简单运
- python 排序和查找算法,,一、搜索1.顺序查找
- pycharm 添加python包 举例cv2,,环境:ubuntu1
- Python并发编程—进程池,,进程池实现1.必要性
- 41、python基础学习-re模块,,就其本质而言,正则表
- Python分词、情感分析工具——SnowNLP,pythonsnownlp,本文内
- python中的高阶函数,python高阶函数,1.高阶函数-实参是
- python 获取复数的实部虚部,,#Initializ
评论关闭