python并发编程之多进程2-------------数据共享及进程池和回调函数
一、数据共享
1.进程间的通信应该尽量避免共享数据的方式
2.进程间的数据是独立的,可以借助队列或管道实现通信,二者都是基于消息传递的。
虽然进程间数据独立,但可以用过Manager实现数据共享,事实上Manager的功能远不止于此。
命令就是一个程序,按回车就会执行(这个只是在windows情况下)
tasklist 查看进程
tasklist | findstr pycharm #(findstr是进行过滤的),|就是管道(tasklist执行的内容就放到管道里面了,
管道后面的findstr pycharm就接收了)
3.(IPC)进程之间的通信有两种实现方式:管道和队列
from multiprocessing import Manager,Process,Lock
def work(dic,mutex):
# mutex.acquire()
# dic['count']-=1
# mutex.release()
# 也可以这样加锁
with mutex:
dic['count'] -= 1
if __name__ == '__main__':
mutex = Lock()
m = Manager() #实现共享,由于字典是共享的字典,所以得加个锁
share_dic = m.dict({'count':100})
p_l = []
for i in range(100):
p = Process(target=work,args=(share_dic,mutex))
p_l.append(p) #先添加进去
p.start()
for i in p_l:
i.join()
print(share_dic)
# 共享就意味着会有竞争,
数据共享
二、进程池
在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。多进程是实现并发的手段之一,需要注意的问题是:
- 很明显需要并发执行的任务通常要远大于核数
- 一个操作系统不可能无限开启进程,通常有几个核就开几个进程
- 进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行)
例如当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个。。。手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。
那么什么是进程池呢?进程池就是控制进程数目
ps:对于远程过程调用的高级应用程序而言,应该使用进程池,Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,就重用进程池中的进程。
进程池的结构:
创建进程池的类:如果指定numprocess为3,则进程池会从无到有创建三个进程,然后自始至终使用这三个进程去执行所有任务,不会开启其他进程
1.创建进程池
Pool([numprocess [,initializer [, initargs]]]):创建进程池
2.参数介绍
numprocess:要创建的进程数,如果省略,将默认为cpu_count()的值,可os.cpu_count()查看
initializer:是每个工作进程启动时要执行的可调用对象,默认为None
initargs:是要传给initializer的参数组
3.方法介绍
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():关闭进程池,防止进一步操作。禁止往进程池内在添加任务(需要注意的是一定要写在close()的上方)
P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用
应用1:
from multiprocessing import Pool
import os,time
def task(n):
print('[%s] is running'%os.getpid())
time.sleep(2)
print('[%s] is done'%os.getpid())
return n**2
if __name__ == '__main__':
# print(os.cpu_count()) #查看cpu个数
p = Pool(4) #最大四个进程
for i in range(1,7):#开7个任务
res = p.apply(task,args=(i,)) #同步的,等着一个运行完才执行另一个
print('本次任务的结束:%s'%res)
p.close()#禁止往进程池内在添加任务
p.join() #在等进程池
print('主')
apply同步进程池(阻塞)(串行)
# ----------------
# 那么我们为什么要用进程池呢?这是因为进程池使用来控制进程数目的,
# 我们需要几个就开几个进程。如果不用进程池实现并发的话,会开很多的进程
# 如果你开的进程特别多,那么你的机器就会很卡,所以我们把进程控制好,用几个就
# 开几个,也不会太占用内存
from multiprocessing import Pool
import os,time
def walk(n):
print('task[%s] running...'%os.getpid())
time.sleep(3)
return n**2
if __name__ == '__main__':
p = Pool(4)
res_obj_l = []
for i in range(10):
res = p.apply_async(walk,args=(i,))
# print(res) #打印出来的是对象
res_obj_l.append(res) #那么现在拿到的是一个列表,怎么得到值呢?我们用个.get方法
p.close() #禁止往进程池里添加任务
p.join()
# print(res_obj_l)
print([obj.get() for obj in res_obj_l]) #这样就得到了
apply_async异步进程池(非阻塞)(并行)
那么什么是同步,什么是异步呢?
同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去
异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
什么是串行,什么是并行呢?
举例:能并排开几辆车的就可以说是“并行”,只能一辆一辆开的就属于“串行”了。很明显,并行的速度要比串行的快得多。(并行互不影响,串行的等着一个完了才能接着另一个)
应用2:
使用进程池维护固定数目的进程(以前客户端和服务端的改进)
from socket import *
from multiprocessing import Pool
s = socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #端口重用
s.bind(('127.0.0.1',8081))
s.listen(5)
print('start running...')
def talk(coon,addr):
while True: # 通信循环
try:
cmd = coon.recv(1024)
print(cmd.decode('utf-8'))
if not cmd: break
coon.send(cmd.upper())
print('发送的是%s'%cmd.upper().decode('utf-8'))
except Exception:
break
coon.close()
if __name__ == '__main__':
p = Pool(4)
while True:#链接循环
coon,addr = s.accept()
print(coon,addr)
p.apply_async(talk,args=(coon,addr))
s.close()
#因为是循环,所以就不用p.join了
服务端
from socket import *
c = socket(AF_INET,SOCK_STREAM)
c.connect(('127.0.0.1',8081))
while True:
cmd = input('>>:').strip()
if not cmd:continue
c.send(cmd.encode('utf-8'))
data = c.recv(1024)
print('接受的是%s'%data.decode('utf-8'))
c.close()
客户端
三、回调函数
回调函数什么时候用?(回调函数在爬虫中最常用)
造数据的非常耗时
处理数据的时候不耗时 你下载的地址如果完成了,就自动提醒让主进程解析
谁要是好了就通知解析函数去解析(回调函数的强大之处)
需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数
我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。
from multiprocessing import Pool
import requests
import os
import time
def get_page(url):
print('<%s> is getting [%s]' %(os.getpid(),url))
response = requests.get(url) #得到地址
time.sleep(2)
print('<%s> is done [%s]'%(os.getpid(),url))
return {'url':url,'text':response.text}
def parse_page(res):
'''解析函数'''
print('<%s> parse [%s]'%(os.getpid(),res['url']))
with open('db.txt','a') as f:
parse_res = 'url:%s size:%s\n' %(res['url'],len(res['text']))
f.write(parse_res)
if __name__ == '__main__':
p = Pool(4)
urls = [
'https://www.baidu.com',
'http://www.openstack.org',
'https://www.python.org',
'https://help.github.com/',
'http://www.sina.com.cn/'
]
for url in urls:
obj = p.apply_async(get_page,args=(url,),callback=parse_page)
p.close()
p.join()
print('主',os.getpid()) #都不用.get()方法了
回调函数(下载网页的小例子)
如果在主进程中等待进程池中所有任务都执行完毕后,再统一处理结果,则无需回调函数
from multiprocessing import Pool
import requests
import os
def get_page(url):
print('<%os> get [%s]' %(os.getpid(),url))
response = requests.get(url) #得到地址 response响应
return {'url':url,'text':response.text}
if __name__ == '__main__':
p = Pool(4)
urls = [
'https://www.baidu.com',
'http://www.openstack.org',
'https://www.python.org',
'https://help.github.com/',
'http://www.sina.com.cn/'
]
obj_l= []
for url in urls:
obj = p.apply_async(get_page,args=(url,))
obj_l.append(obj)
p.close()
p.join()
print([obj.get() for obj in obj_l])
下载网页小例子(无需回调函数)
python并发编程之多进程2-------------数据共享及进程池和回调函数的更多相关文章
- python并发编程之多进程2-(数据共享及进程池和回调函数)
一.数据共享 1.进程间的通信应该尽量避免共享数据的方式 2.进程间的数据是独立的,可以借助队列或管道实现通信,二者都是基于消息传递的. 虽然进程间数据独立,但可以用过Manager实现数据共享,事实 ...
- python并发编程之多进程2数据共享及进程池和回调函数
一.数据共享 尽量避免共享数据的方式 可以借助队列或管道实现通信,二者都是基于消息传递的. 虽然进程间数据独立,但可以用过Manager实现数据共享,事实上Manager的功能远不止于此. 命令就是一 ...
- Python进阶(4)_进程与线程 (python并发编程之多进程)
一.python并发编程之多进程 1.1 multiprocessing模块介绍 由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大 ...
- Python并发编程__多进程
Python并发编程_多进程 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大 ...
- python并发编程02 /多进程、进程的创建、进程PID、join方法、进程对象属性、守护进程
python并发编程02 /多进程.进程的创建.进程PID.join方法.进程对象属性.守护进程 目录 python并发编程02 /多进程.进程的创建.进程PID.join方法.进程对象属性.守护进程 ...
- Python 3 并发编程多进程之进程池与回调函数
Python 3 进程池与回调函数 一.进程池 在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间.多进程是实现并发的手段之一,需要注意 ...
- python语法基础-并发编程-进程-进程池以及回调函数
############### 进程池 ############## """ 进程池的概念 为什么会有进程池? 1,因为每次开启一个进程,都需要创建一个内存空间 ...
- python并发编程之多进程(三):共享数据&进程池
一,共享数据 展望未来,基于消息传递的并发编程是大势所趋 即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合 通过消息队列交换数据.这样极大地减少了对使用锁定和其他同步手段的需求, 还可以扩展 ...
- 28 python 并发编程之多进程
一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程.P ...
- 二 python并发编程之多进程-重点
一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程.P ...
随机推荐
- delegate事件委托
下面举个例子 我们希望通过点击使得点击的li标签变红 <body style="height:2000px;"> <ul> <li>1111&l ...
- L3-1 二叉搜索树的结构 (30 分)
讲解的很不错的链接:https://blog.csdn.net/chudongfang2015/article/details/79446477#commentBox 题目链接:https://pin ...
- 新语法11. – LINQ
LINQ分组: IEnumerable<IGrouping< group dog by dog.Age; 遍历分组: foreach (IGrouping<int, Dog> ...
- openstack Q版部署-----网络服务(neutron)安装部署(7)
一.数据库配置(控制节点) 控制节点创建数据库: CREATE DATABASE neutron; GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'lo ...
- zookeeper的ACL权限控制
ACL:Access Control List 访问控制列表 1. 简介 0.概述 ACL 权限控制,使用:scheme:id:perm 来标识,主要涵盖 3 个方面: 权限模式(Scheme): ...
- Queue和BlockingQueue的使用以及使用BlockingQueue实现生产者-消费者
Java提供了两种新的容器类型:Queue和BlockingQueue. Queue用于保存一组等待处理的元素.它提供了几种实现,包括:ConcurrentLinkedQueue,这是一个先进先出的并 ...
- PHP Tp5.0 PHPExcel 导出操作
1.导出 excel 方法 2.导出方法(简单快速)
- java知识点1
本系列文章源自大神--纯洁的微笑的博客 http://www.cnblogs.com/ityouknow/ 基础篇 JVM JVM内存结构 堆.栈.方法区.直接内存.堆和栈区别 内存结构图 控制参数 ...
- Redis主从哨兵和集群搭建
主从配置 哨兵配置 集群配置 1.主从: 国王和丞相,国王权力大(读写),丞相权利小(读) 2.哨兵: 国王和王子,国王死了(主服务挂掉),王子继位(从服务变主服务) 3.集群: 国王和国王,一个国王 ...
- Matplotlib(used in paper)
1. 转自:matplotlib 使用 plt.savefig() 输出图片去除旁边的空白区域 注:如果不采用 subplot_adjust + margin(0,0),而是在fig.savefig( ...