之后的进程池使用的是 ProcessPoolExecutor,它的底层使用的就是pool

为什么要有进程池?进程池的概念。

在程序实际处理问题过程中,忙时会有成千上万的任务需要被执行,闲时可能只有零星任务。那么在成千上万个任务需要被执行的时候,

我们就需要去创建成千上万个进程么?首先,创建进程需要消耗时间,销毁进程也需要消耗时间。第二即便开启了成千上万的进程,

操作系统也不能让他们同时执行,这样反而会影响程序的效率。因此我们不能无限制的根据任务开启或者结束进程。那么我们要怎么做呢?

在这里,要给大家介绍一个进程池的概念,定义一个池子,在里面放上固定数量的进程,有需求来了,就拿一个池中的进程来处理任务,

等到处理完毕,进程并不关闭,而是将进程再放回进程池中继续等待任务。如果有很多任务需要执行,池中的进程数量不够,

任务就要等待之前的进程执行任务完毕归来,拿到空闲进程才能继续执行。也就是说,池中进程的数量是固定的,

那么同一时间最多有固定数量的进程在运行。这样不会增加操作系统的调度难度,还节省了开闭进程的时间,也一定程度上能够实现并发效果。

Pool([numprocess  [,initializer [, initargs]]]):创建进程池

创建进程池的类:如果指定numprocess为3,则进程池会从无到有创建三个进程,然后自始至终使用这三个进程去执行所有任务,不会开启其他进程

1 numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值
2 initializer:是每个工作进程启动时要执行的可调用对象,默认为None
3 initargs:是要传给initializer的参数组

参数介绍

p.apply(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。
'''需要强调的是:此操作并不会在所有池工作进程中并执行func函数。如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用p.apply_async()''' p.apply_async(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。
'''此方法的结果是AsyncResult类的实例,callback是可调用对象,接收输入参数。当func的结果变为可用时,将理解传递给callback。callback禁止执行任何阻塞操作,否则将接收其他异步操作中的结果。''' p.close():关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成 P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用

主要方法

方法apply_async()和map_async()的返回值是AsyncResul的实例obj。实例具有以下方法
obj.get():返回结果,如果有必要则等待结果到达。timeout是可选的。如果在指定时间内还没有到达,将引发一场。如果远程操作中引发了异常,它将在调用此方法时再次被引发。
obj.ready():如果调用完成,返回True
obj.successful():如果调用完成且没有引发异常,返回True,如果在结果就绪之前调用此方法,引发异常
obj.wait([timeout]):等待结果变为可用。
obj.terminate():立即终止所有工作进程,同时不执行任何清理或结束任何挂起工作。如果p被垃圾回收,将自动调用此函数

其他:了解

简单使用

import time
from multiprocessing import Pool
def fc(i):
time.sleep(0.5)
print('func%s'%i) if __name__ == '__main__':
p = Pool(5)
for i in range(5):
p.apply(func=fc,args=(i,)) # 同步调用

同步调用apply

import time
import random
import os
from multiprocessing import Pool def work(n):
print('%s run' %os.getpid())
time.sleep(3)
return n**2 if __name__ == '__main__':
p=Pool(3) #进程池中从无到有创建三个进程,以后一直是这三个进程在执行任务
res_l=[]
for i in range(10):
res=p.apply(work,args=(i,)) #同步调用,直到本次任务执行完毕拿到res,等待任务work执行的过程中可能有阻塞也可能没有阻塞,但不管该任务是否存在阻塞,同步调用都会在原地等着,只是等的过程中若是任务发生了阻塞就会被夺走cpu的执行权限
res_l.append(res)
print(res_l) '''
8440 run
17352 run
7280 run
8440 run
17352 run
7280 run
8440 run
17352 run
7280 run
8440 run
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
#这里面的结果每三个进程执行一次
'''

同步调用apply

异步调用apply_async

import os
import time
import random
from multiprocessing import Pool def work(n):
print('%s run' %os.getpid())
time.sleep(random.random())
return n**2 if __name__ == '__main__':
p=Pool(3) #进程池中从无到有创建三个进程,以后一直是这三个进程在执行任务
res_l=[]
for i in range(10):
res=p.apply_async(work,args=(i,)) # 异步运行,根据进程池中有的进程数,每次最多3个子进程在异步执行
# 返回结果之后,将结果放入列表,归还进程,之后再执行新的任务
# 需要注意的是,进程池中的三个进程不会同时开启或者同时结束
# 而是执行完一个就释放一个进程,这个进程就去接收新的任务。
res_l.append(res) # 异步apply_async用法:如果使用异步提交的任务,主进程需要使用jion,等待进程池内任务都处理完,然后可以用get收集结果
# 否则,主进程结束,进程池可能还没来得及执行,也就跟着一起结束了
p.close()
p.join()
for res in res_l:
print(res.get()) '''
992 run
17040 run
16152 run
16152 run
17040 run
992 run
992 run
17040 run
16152 run
992 run
0
1
4
9
16
25
36
49
64
81
'''

详解apply和apply_async

1 apply

from multiprocessing import Process,Pool
import time
def func(msg):
print('msg:',msg)
time.sleep(1)
return msg
if __name__ == '__main__':
pool=Pool(processes=3)
res_l=[]
for i in range(10):
msg='hello %d'%i
res=pool.apply(func,(msg,))
res_l.append(res)
print('===================>')
pool.close()
pool.join()
print(res_l)
for i in res_l:
print(i)
'''
msg: hello 0 #一条一条出现
msg: hello 1
msg: hello 2
msg: hello 3
msg: hello 4
msg: hello 5
msg: hello 6
msg: hello 7
msg: hello 8
msg: hello 9
===================>
['hello 0', 'hello 1', 'hello 2', 'hello 3', 'hello 4', 'hello 5', 'hello 6', 'hello 7', 'hello 8', 'hello 9']
hello 0
hello 1
hello 2
hello 3
hello 4
hello 5
hello 6
hello 7
hello 8
hello 9
'''

2 apply_async

from multiprocessing import Process,Pool
import time def func(msg):
print( "msg:", msg)
time.sleep(1)
return msg if __name__ == "__main__":
pool = Pool(processes = 3)
res_l=[]
for i in range(10):
msg = "hello %d" %(i)
res=pool.apply_async(func, (msg, )) #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
res_l.append(res)
print("==============================>") #没有后面的join,或get,则程序整体结束,进程池中的任务还没来得及全部执行完也都跟着主进程一起结束了 pool.close() #关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成
pool.join() #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束 print(res_l) #看到的是<multiprocessing.pool.ApplyResult object at 0x10357c4e0>对象组成的列表,而非最终的结果,但这一步是在join后执行的,证明结果已经计算完毕,剩下的事情就是调用每个对象下的get方法去获取结果
for i in res_l:
print(i.get()) #使用get来获取apply_aync的结果,如果是apply,则没有get方法,因为apply是同步执行,立刻获取结果,也根本无需get '''
==============================>
msg: hello 0 #三条三条出现
msg: hello 1
msg: hello 2
msg: hello 3
msg: hello 4
msg: hello 5
msg: hello 6
msg: hello 7
msg: hello 8
msg: hello 9
[<multiprocessing.pool.ApplyResult object at 0x000001E1EEA8B9E8>, <multiprocessing.pool.ApplyResult object at 0x000001E1EEA8BAC8>, <multiprocessing.pool.ApplyResult object at 0x000001E1EEA8BBA8>, <multiprocessing.pool.ApplyResult object at 0x000001E1EEA8BC88>, <multiprocessing.pool.ApplyResult object at 0x000001E1EEA8BD68>, <multiprocessing.pool.ApplyResult object at 0x000001E1EEA8BE80>, <multiprocessing.pool.ApplyResult object at 0x000001E1EEA8BF98>, <multiprocessing.pool.ApplyResult object at 0x000001E1EEA9B0F0>, <multiprocessing.pool.ApplyResult object at 0x000001E1EEA9B208>, <multiprocessing.pool.ApplyResult object at 0x000001E1EEA9B320>]
hello 0 #必须使用get取值
hello 1
hello 2
hello 3
hello 4
hello 5
hello 6
hello 7
hello 8
hello 9
'''

练习:使用进程池维护固定数目的进程

#Pool内的进程数默认是cpu核数,假设为4(查看方法os.cpu_count())
#开启6个客户端,会发现2个客户端处于等待状态
#在每个进程内查看pid,会发现pid使用为4个,即多个客户端公用4个进程
from socket import *
from multiprocessing import Pool
import os server=socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5) def talk(conn,client_addr):
print('进程pid: %s' %os.getpid())
while True:
try:
msg=conn.recv(1024)
if not msg:break
conn.send(msg.upper())
except Exception:
break if __name__ == '__main__':
p=Pool() #默认为cpu的核数,根据自己计算机情况来定
while True:
conn,client_addr=server.accept()
p.apply_async(talk,args=(conn,client_addr))
# p.apply(talk,args=(conn,client_addr)) #同步的话,则同一时间只有一个客户端能访问 它们始终都是在进程池中随机挑选一个进程响应客户端

服务端

from socket import *

client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080)) while True:
msg=input('>>: ').strip()
if not msg:continue client.send(msg.encode('utf-8'))
msg=client.recv(1024)
print(msg.decode('utf-8'))

客户端

发现:并发开启多个客户端,服务端同一时间只有3个不同的pid,干掉一个客户端,另外一个客户端才会进来,被3个进程之一处理

回调函数

需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数

我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。

进程池的数量一般为CPU的个数加1

下面来看一个例子

import os
from urllib.request import urlopen
from multiprocessing import Pool def get_url(url):
print('-->',url,os.getpid(),'get_url进程')
ret = urlopen(url) # 打开url
content = ret.read() # 读取网页内容
return url def call(url): # 回调函数
#分析
print(url,os.getpid(),'回调函数') if __name__ == '__main__':
print(os.getpid(),'主进程') # 主进程id
l = [
'http://www.baidu.com',
'http://www.sina.com',
'http://www.sohu.com',
'http://www.sogou.com',
'http://www.qq.com',
'http://www.bilibili.com',
]
p = Pool(5)
ret_l = []
for url in l:
ret = p.apply_async(func=get_url,args=[url,],callback=call) # 异步
ret_l.append(ret) # 将进程追加到列表中
for ret in ret_l:ret.get() # 获取进程返回值

上面的args=[url,]可以换成()

结果:

'''
15652 主进程
---> http://www.baidu.com 11672 get_url进程
---> http://www.sina.com 8968 get_url进程
---> http://www.sohu.com 17764 get_url进程
---> http://www.sogou.com 5852 get_url进程
---> http://www.qq.com 3260 get_url进程
---> http://www.bilibili.com 11672 get_url进程 #由于进程池数量为5,这个pid复用
http://www.baidu.com 15652 回调函数 #下面的pid都是主进程的pid,证明回调函数是在主进程执行的
http://www.sohu.com 15652 回调函数
http://www.qq.com 15652 回调函数
http://www.sogou.com 15652 回调函数
http://www.bilibili.com 15652 回调函数
http://www.sina.com 15652 回调函数
#get_url函数中并没有打印,但是它的return结果传递给了call回调函数执行
'''

结论:在进程池中,一个任务对应的函数在执行完毕之后,其返回值会自动作为参数返回给回调函数

回调函数在主进程中执行的,回调函数是瞬间执行的,网络延时最占时长

如果在主进程中等待进程池中所有任务都执行完毕后,再统一处理结果,则无需回调函数

from multiprocessing import Pool
import time,random,os def work(n):
time.sleep(1)
return n**2
if __name__ == '__main__':
p=Pool() res_l=[]
for i in range(10):
res=p.apply_async(work,args=(i,))
res_l.append(res) p.close()
p.join() #等待进程池中所有进程执行完毕 nums=[]
for res in res_l:
nums.append(res.get()) #拿到所有结果
print(nums) #主进程拿到所有的处理结果,可以在主进程中进行统一进行处理

进程池----Pool(老的方式)----回调的更多相关文章

  1. python 全栈开发,Day40(进程间通信(队列和管道),进程间的数据共享Manager,进程池Pool)

    昨日内容回顾 进程 multiprocess Process —— 进程 在python中创建一个进程的模块 start daemon 守护进程 join 等待子进程执行结束 锁 Lock acqui ...

  2. python学习笔记——multiprocessing 多进程组件 进程池Pool

    1 进程池Pool基本概述 在使用Python进行系统管理时,特别是同时操作多个文件目录或者远程控制多台主机,并行操作可以节约大量时间,如果操作的对象数目不大时,还可以直接适用Process类动态生成 ...

  3. python全栈开发,Day40(进程间通信(队列和管道),进程间的数据共享Manager,进程池Pool)

    昨日内容回顾 进程 multiprocess Process —— 进程 在python中创建一个进程的模块 start daemon 守护进程 join 等待子进程执行结束 锁 Lock acqui ...

  4. [转]Python多进程并发操作中进程池Pool的应用

    Pool类 在使用Python进行系统管理时,特别是同时操作多个文件目录或者远程控制多台主机,并行操作可以节约大量的时间.如果操作的对象数目不大时,还可以直接使用Process类动态的生成多个进程,十 ...

  5. Python多进程并发操作中进程池Pool的应用

    Pool类 在使用Python进行系统管理时,特别是同时操作多个文件目录或者远程控制多台主机,并行操作可以节约大量的时间.如果操作的对象数目不大时,还可以直接使用Process类动态的生成多个进程,十 ...

  6. python 使用进程池Pool进行并发编程

    进程池Pool 当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到mu ...

  7. python 进程池pool简单使用

    平常会经常用到多进程,可以用进程池pool来进行自动控制进程,下面介绍一下pool的简单使用. 需要主动是,在Windows上要想使用进程模块,就必须把有关进程的代码写if __name__ == ‘ ...

  8. 多任务-进程之进程池Pool

    1.什么是池? 首先从字面上看,池代表着一个容器,用来承载着某些内容的容器,了解到这里,就对进程池有了一个初步的轮廓. 2.什么是进程池Pool? (1)利用现实中的事物来理解: 对于小白初学者,接触 ...

  9. Python多进程库multiprocessing创建进程以及进程池Pool类的使用

    问题起因最近要将一个文本分割成好几个topic,每个topic设计一个regressor,各regressor是相互独立的,最后汇总所有topic的regressor得到总得预测结果.没错!类似bag ...

随机推荐

  1. 有道词典 安卓版 更新日志 - imsoft.cnblogs

    手机词典 更新日志: 2014年4月23日 v5.0.4版本 ·         新增“全球发音”功能,带来世界各国的英文口音,体验原汁原味的英语发音: ·         新增“发现频道”,打造移动 ...

  2. 设置ip地址、掩码、网关、DNS

    @echo offcolor f8mode con cols=40 lines=8echo.echo.echo      设置IP为:echo.set /p ip=              192. ...

  3. EasyTouch基本用法

    EasyTouch基本用法 本文提供全流程,中文翻译.Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) 1 hierarchy (层次面 ...

  4. BZOJ4916: 神犇和蒟蒻【杜教筛】

    Description 很久很久以前,有一只神犇叫yzy; 很久很久之后,有一只蒟蒻叫lty; Input 请你读入一个整数N;1<=N<=1E9,A.B模1E9+7; Output 请你 ...

  5. qwb的骚扰

    题目描述 自从学姐拒绝了qwb之后,qwb开始了疯狂的骚扰.qwb来到了一个公共电话亭,他摸摸口袋只有n元钱. 已知该公用电话的规则是,前3分钟一共收费x元(不到3分钟也要收x元),超过3分钟每分钟收 ...

  6. UWP 在 WebView 中执行 JavaScript 代码(用于模拟用户输入等)

    UWP 中使用 WebView 时可以在网页中额外执行一些代码.于是你几乎可以在网页上做任何事情,那些你可以在浏览器控制台中做的事情. 本文将介绍做法. 本文内容 准备环境 执行 JavaScript ...

  7. AGC014E Blue and Red Tree

    题意 There is a tree with \(N\) vertices numbered \(1\) through \(N\). The \(i\)-th of the \(N−1\) edg ...

  8. ES6必知必会 (三)—— 数组和对象的拓展

    数组的扩展 1.拓展运算符('...'),它相当于rest参数的逆运算,用于将一个数组转换为用逗号分隔的参数序列: console.log(...[1, 2, 3]) // 1 2 3 console ...

  9. 如何判断一个请求是否为AJAX请求

    普通请求与ajax请求的报文头不一样,通过如下 String requestType = request.getHeader("X-Requested-With");  如果req ...

  10. FastAdmin 插件的 Git 开发流程(简明)

    FastAdmin 插件的 Git 开发流程(简明) cms zip 安装 包安装 删除 addons 里的 cms 使用 mklink 软链接到 cms 插件 Git 仓库 修改 cms 插件 gi ...