一、管道(Pipe)(了解) (详情参考:https://www.cnblogs.com/clschao/articles/9629392.html)

  进程间通信(IPC)方式二:管道(不推荐使用,了解即可),会导致数据不安全的情况出现,后面我们会说到为什么会带来数据 不安全的问题。

#创建管道的类:
Pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道
#参数介绍:
dumplex:默认管道是全双工的,如果将duplex射成False,conn1只能用于接收,conn2只能用于发送。
#主要方法:
conn1.recv():接收conn2.send(obj)发送的对象。如果没有消息可接收,recv方法会一直阻塞。如果连接的另外一端已经关闭,那么recv方法会抛出EOFError。
conn1.send(obj):通过连接发送对象。obj是与序列化兼容的任意对象
#其他方法:
conn1.close():关闭连接。如果conn1被垃圾回收,将自动调用此方法
conn1.fileno():返回连接使用的整数文件描述符
conn1.poll([timeout]):如果连接上的数据可用,返回True。timeout指定等待的最长时限。如果省略此参数,方法将立即返回结果。如果将timeout射成None,操作将无限期地等待数据到达。

管道基本介绍

  注意:管道之间不允许相同端口之间通信,只能是一个进程conn1 <--->另一进程的conn2或者一个进程conn2 <--->另一进程的conn1进行通信。recv未结收到消息,会阻塞。发送端管道端口关闭,再发送消息,报错EOFError,接收端堵塞,则报错:OSError。

from multiprocessing import Pipe,Process

def func(conn1,):
# conn2.close()
msg = conn1.recv()
print('>>>>>',msg) if __name__ == '__main__':
conn1,conn2 = Pipe() p = Process(target=func,args=(conn1,))
p.start()
# conn1.close()
conn2.close()
conn2.send('小鬼!') print('主进程结束') #EOFError #发送端堵塞,无法发送
#OSError #接收端堵塞,无法接受

send、recv、close

关于管道会造成数据不安全问题的官方解释:
The two connection objects returned by Pipe() represent the two ends of the pipe. Each connection object has send() and recv() methods (among others). Note that data in a pipe may become corrupted if two processes (or threads) try to read from or write to the same end of the pipe at the same time. Of course there is no risk of corruption from processes using different ends of the pipe at the same time. 由Pipe方法返回的两个连接对象表示管道的两端。每个连接对象都有send和recv方法(除其他之外)。注意,如果两个进程(或线程)试图同时从管道的同一端读取或写入数据,那么管道中的数据可能会损坏。当然,在使用管道的不同端部的过程中不存在损坏风险。

管道官方解释

二、数据共享Manager(了解)

  进程之间数据共享的模块之一Manager模块

进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的
虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此 A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies. A manager returned by Manager() will support types list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array.

Manager简介

  Manager使用的基本步骤;

    m=Manager()

    dic=m.dict({"name":"sbalex",}

    之后就可以贡各个进程使用,并修改

    (存在数据安全问题,慎用)

from multiprocessing import Process,Manager,Lock

def func1(dic,loc):
with loc:
dic["num"]-=1 if __name__ == '__main__':
m=Manager()
loc=Lock()
dic=m.dict({"num":100,})
p_lst=[]
for i in range(10):
p=Process(target=func1,args=(dic,loc))
p_lst.append(p)
p.start() # p.join()
[pp.join() for pp in p_lst]
print(dic)

Manage+Lock使用实例

三、进程池Pool(重点)

  

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

  在程序实际处理问题过程中,忙时会有成千上万的任务需要被执行,闲时可能只有零星任务。那么在成千上万个任务需要被执行的时候,我们就需要去创建成千上万个进程么?首先,创建进程需要消耗时间,销毁进程(空间,变量,文件信息等等的内容)也需要消耗时间。第二即便开启了成千上万的进程,操作系统也不能让他们同时执行,维护一个很大的进程列表的同时,调度的时候,还需要进行切换并且记录每个进程的执行节点,也就是记录上下文(各种变量等等乱七八糟的东西,虽然你看不到,但是操作系统都要做),这样反而会影响程序的效率。因此我们不能无限制的根据任务开启或者结束进程。就看我们上面的一些代码例子,你会发现有些程序是不是执行的时候比较慢才出结果,就是这个原因,那么我们要怎么做呢?

  在这里,要给大家介绍一个进程池的概念,定义一个池子,在里面放上固定数量的进程,有需求来了,就拿一个池中的进程来处理任务,等到处理完毕,进程并不关闭,而是将进程再放回进程池中继续等待任务。如果有很多任务需要执行,池中的进程数量不够,任务就要等待之前的进程执行任务完毕归来,拿到空闲进程才能继续执行。也就是说,池中进程的数量是固定的,那么同一时间最多有固定数量的进程在运行。这样不会增加操作系统的调度难度,还节省了开闭进程的时间,也一定程度上能够实现并发效果

  总结:进程池中可以制定创建若干个进程,在程序执行的过程中,即使进程结束了也不会关闭进程,等待下一个任务进入进程池后,拿着空闲的进程继续干活.因此处理一大批任务的时候,只需要创建若干个进程,就可以完成所有的任务,节约了创建和销毁进程的时间,减轻的cpu和硬盘的负担.

  2、multiprocess.Pool 模块

  创建进程池的类:如果指定numprocess为3,则进程池会从无到有创建三个进程,然后自始至终使用这三个进程去执行所有任务(高级一些的进程池可以根据你的并发量,搞成动态增加或减少进程池中的进程数量的操作),不会开启其他进程,提高操作系统效率,减少空间的占用等。

  创建方法:

Pool([numprocess  [,initializer [, initargs]]]):创建进程池
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()之后调用

主要方法介绍

  (1)pool.map(func,可迭代对象) 表示可迭代对象分别把所有的值依次传递给func执行,并创建进程,内部有join机制

from multiprocessing import Process,Pool
import time
def func1(i):
num=i
for j in range(5):
num+=j if __name__ == '__main__':
#不使用进程池
start_time=time.time()
p_lst=[]
for i in range(1000):
p=Process(target=func1,args=(i,))
p_lst.append(p)
p.start() [pp.join() for pp in p_lst]
end_time=time.time()
print("不适用进程池",end_time-start_time) #引入进程池
s_time=time.time()
pool=Pool(8)
pool.map(func1,range(1000)) #自动join,pool.map(函数,可迭代对象)
e_time=time.time()
print("使用进程池>>>",e_time-s_time)

不使用进程池和使用进程池效率对比

执行结果:
不适用进程池 36.224305391311646
使用进程池>>> 0.30788564682006836

结果显示

  

  注意:有一点,map是异步执行的,并且自带close和join

    一般约定俗成的是进程池中的进程数量为CPU的数量,工作中要看具体情况来考量。

  (2)pool.apply(func,不可迭代对象) 表示给func传参并创建进程。

      pool.apply——async(func,不可迭代对象) 表示给func传参并创建进程。

        pool.get()  进程池执行任务返回的是对象,需要用get方法获取返回值的内容。

        pool.close() 不是关闭进程池,而是停止进程池接受新任务,这样才能感知进程池是否把所有任                                                   务执行完毕

        pool.join() 主程序等待进程池执行完所有任务再结束,必须在pool.close之后。

from multiprocessing import Process,Pool
import time
def func(i):
num=0
time.sleep(1)
for j in range(5):
num+=i
return num
if __name__ == '__main__':
pool=Pool(4) #进程池中创建4个进程,以后一直都是这四个进程在执行任务
for i in range(10):
#apply 串行执行
#同步调用,直到本次任务执行完毕拿到ret,等待任务work执行的过程中可能有阻塞也可能没有阻塞
ret=pool.apply(func,args=(i,))#不能传入可迭代对象
print(ret)

apply 同步(串口)执行任务

import time
from multiprocessing import Process,Pool def func(i):
num=0
time.sleep(1)
for j in range(5):
num+=i
# print(num)
return num
if __name__ == '__main__':
pool=Pool(4)
lst=[]
for i in range(10):
ret=pool.apply_async(func,args=(i,))
# print(ret.get()) #放在for 循环中,创建一个进程取一个值
lst.append(ret) pool.close() #不是关闭进程池,而是结束进程池接收任务,确保没有新任务再提交过来。
# 感知进程池中的任务已经执行结束,只有当没有新的任务添加进来的时候,才能感知到任务结束了,所以在join之前必须加上close方法
pool.join()
# 使用get来获取apply_aync的结果,如果是apply,则没有get方法,因为apply是同步执行,立刻获取结果,也根本无需get
[print(el.get()) for el in lst]

apply_async 异步执行进程

  注意:异步apply_async用法:如果使用异步提交的任务,主程序需要用join,等待进程池中的任务处理完毕后才可以用get获取结果。使用join之前,必须使用pool.close,结束进程接受任务。否则无法感知是否会有进程进入进程池,就不会知道何时进程池没有任务。

#一:使用进程池(异步调用,apply_async)
#coding: utf-8
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)
# s = res.get() #如果直接用res这个结果对象调用get方法获取结果的话,这个程序就变成了同步,因为get方法直接就在这里等着你创建的进程的结果,第一个进程创建了,并且去执行了,那么get就会等着第一个进程的结果,没有结果就一直等着,那么主进程的for循环是无法继续的,所以你会发现变成了同步的效果
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 #二:使用进程池(同步调用,apply)
#coding: utf-8
from multiprocessing import Process,Pool
import time def func(msg):
print( "msg:", msg)
time.sleep(0.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, )) #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
res_l.append(res) #同步执行,即执行完一个拿到结果,再去执行另外一个
print("==============================>")
pool.close()
pool.join() #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束 print(res_l) #看到的就是最终的结果组成的列表
for i in res_l: #apply是同步的,所以直接得到结果,没有get()方法
print(i)

详解apply和apply_async

  #进程池版socket聊天代码:

#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):
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(4)
while True:
conn,*_=server.accept()
p.apply_async(talk,args=(conn,))
# p.apply(talk,args=(conn,client_addr)) #同步的话,则同一时间只有一个客户端能访问 server端:tcp_server.py

服务器

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)回调函数

需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数,这是进程池特有的,普通进程没有这个机制,但是我们也可以通过进程通信来拿到返回值,进程池的这个回调也是进程通信的机制完成的。

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

回调函数简介

  注意:回调函数是在主进程执行的,但是如果主进程未接受到pool.apply_async函数内部的的回调信息,则可能会提前关闭子进程.

import time
from multiprocessing import Pool,Process import os def func(n):
# print('xxxxxxxxxx')
print('子进程的pid :',os.getpid())
return n*n,'约吗' def call_back_func(x):
# print(x) #(9, '约吗')
print('call_back pid ::',os.getpid())
print(x[0]) if __name__ == '__main__':
pool = Pool(4)
pool.apply_async(func,args=(3,),callback=call_back_func) print('主进程的pid:',os.getpid())
pool.close()
pool.join()

验证回调函数在主进程执行

  代码显示:回调函数与主进程函数的pid号一致,说明了回调函数在主进程中执行.

  

python 管道、数据共享、进程池的更多相关文章

  1. python摸爬滚打之day032 管道 数据共享 进程池

    1.进程池 当有成千上万个任务需要被执行的时候,有了进程池我们就不必去创建大量的进程. 首先,创建进程需要消耗时间,销毁进程(空间,变量,文件信息等等的内容)也需要消耗时间, 第二即便开启了成千上万的 ...

  2. python中的进程池

    1.进程池的概念 python中,进程池内部会维护一个进程序列.当需要时,程序会去进程池中获取一个进程. 如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止. 2.进程池 ...

  3. python中的进程池:multiprocessing.Pool()

    python中的进程池: 我们可以写出自己希望进程帮助我们完成的任务,然后把任务批量交给进程池 进程池帮助我们创建进程完成任务,不需要我们管理.进程池:利用multiprocessing 下的Pool ...

  4. Python中的进程池与线程池(包含代码)

    Python中的进程池与线程池 引入进程池与线程池 使用ProcessPoolExecutor进程池,使用ThreadPoolExecutor 使用shutdown 使用submit同步调用 使用su ...

  5. 进程同步控制(锁,信号量,事件), 进程通讯(队列和管道,生产者消费者模型) 数据共享(进程池和mutiprocess.Pool模块)

    参考博客 https://www.cnblogs.com/xiao987334176/p/9025072.html#autoid-1-1-0 进程同步(multiprocess.Lock.Semaph ...

  6. python多进程,进程池,数据共享,进程通信,分布式进程

    一.操作系统中相关进程的知识   Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊.普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前 ...

  7. Python 多进程和进程池

    一,前言 进程:是程序,资源集合,进程控制块组成,是最小的资源单位 特点:就对Python而言,可以实现真正的并行效果 缺点:进程切换很容易消耗cpu资源,进程之间的通信相对线程来说比较麻烦 线程:是 ...

  8. day32 通道 数据共享 进程池

    1.管道 格式: conn1,conn2 = Pipe() 管道的两端可以进行全双工通信   如图 进程2创建了管道,它就拥有管道两端的信息,每个端点都能收发信息,它把端点信息传给进程1和进程3 ,它 ...

  9. python多进程,以及进程池并发

    模拟多进程 #!/usr/bin/env python#-*- coding:utf-8 -*-import timefrom multiprocessing import Process def s ...

  10. python中的进程池和线程池

    Python标准模块-concurrent.futures #1 介绍 concurrent.futures模块提供了高度封装的异步调用接口 ThreadPoolExecutor:线程池,提供异步调用 ...

随机推荐

  1. 第一册:lesson seventy seven。

    原文:terrible toothache. Good morning Mr.Croft. Good morning nurse. I want to see the dentist,please. ...

  2. 【Java每日一题】20170220

    20170217问题解析请点击今日问题下方的“[Java每日一题]20170220”查看(问题解析在公众号首发,公众号ID:weknow619) package Feb2017; import jav ...

  3. Centos6.5安装Redis3.0备忘记录

    Centos6.5安装Redis3.0 1. 安装C编译环境 首先需要安装编译Redis的C环境,在命令行执行以下命令: [root@itzhouq32 tools] yum install gcc- ...

  4. 兼容发布&小流量概述

    背景 re消息limit上线之前有这样的问题: 1.存量用户,会感知到新功能: 2.后端.前端上线间隔期间,如果没做兼容,涉及到接口数据格式变更,会导致C端拉取数据报错: 3.改模板配置,会导致老信息 ...

  5. 微信小程序性能优化技巧

    摘要: 如果小程序不够快,还要它干嘛? 原文:微信小程序性能优化方案--让你的小程序如此丝滑 作者:杜俊成要好好学习 Fundebug经授权转载,版权归原作者所有. 微信小程序如果想要优化性能,有关键 ...

  6. 快速掌握JavaScript面试基础知识(三)

    译者按: 总结了大量JavaScript基本知识点,很有用! 原文: The Definitive JavaScript Handbook for your next developer interv ...

  7. WORLD F4快捷重复上一步操作

    只需做一次动作,后面就直接按F4即可重复上一次操作.

  8. thinkphp模板中,checkbox回显问题

    thinkphp 模板里面可以这样写包含操作 //in 标签 <in name="变量名" value="值1,值2,...">要输出的内容< ...

  9. 包含min函数的栈 ,二叉树的镜像

    包含min函数的栈 问题 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1)). 代码 # -*- coding:utf-8 -*- class Sol ...

  10. 解决ui-router路由监听$stateChangeStart、$stateChangeSuccess、$stateChangeError不执行的问题

    问题解答 angular1项目导入ui-router之后,使用路由监听,代码如下 angular.module('app', ['ui.router', 'ui.router.state.events ...