Day035--Python--管道, Manager, 进程池, 线程切换
管道
#创建管道的类:
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.recv_bytes([maxlength]):接收c.send_bytes()方法发送的一条完整的字节消息。maxlength指定要接收的最大字节数。如果进入的消息,超过了这个最大值,将引发IOError异常,并且在连接上无法进行进一步读取。如果连接的另外一端已经关闭,再也不存在任何数据,将引发EOFError异常。
conn.send_bytes(buffer [, offset [, size]]):通过连接发送字节数据缓冲区,buffer是支持缓冲区接口的任意对象,offset是缓冲区中的字节偏移量,而size是要发送字节数。结果数据以单条消息的形式发出,然后调用c.recv_bytes()函数进行接收 conn1.recv_bytes_into(buffer [, offset]):接收一条完整的字节消息,并把它保存在buffer对象中,该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。offset指定缓冲区中放置消息处的字节位移。返回值是收到的字节数。如果消息长度大于可用的缓冲区空间,将引发BufferTooShort异常。 管道介绍
了解
from multiprocessing import Process, Pipe conn1, conn2 = Pipe()
conn1.send('你好')
print('>>>>>>>>>>>')
msg = conn2.recv()
print(msg)
from multiprocessing import Process, Pipe
def func1(conn2):
msg = conn2.recv()
print(msg) if __name__ == '__main__':
conn1, conn2 = Pipe()
p = Process(target=func1, args=(conn2,))
p.start()
conn1.send('你好啊,我叫赛利亚')
管道错误模拟: 管道关闭, 异常处理
from multiprocessing import Process, Pipe def func(conn2):
while 1:
try:
# 如果管道一端关闭了, 另外一端接收消息时会报错, 要使用异常处理
msg = conn2.recv()
print(msg)
except EOFError:
print('对方管道已关闭')
conn2.close()
break if __name__ == '__main__':
conn1, conn2 = Pipe()
p = Process(target=func, args=(conn2,))
p.start()
conn1.send('你好啊')
conn1.close()
# conn1.recv() # OSError: handle is closed
数据共享
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.
from multiprocessing import Process, Manager def func(m_dic):
m_dic['name'] = '大猪蹄子' # 修改共享数据 if __name__ == '__main__':
m = Manager()
m_dic = m.dict({'name': '大佬'}) # 创建共享数据
print('原始>>>', m_dic) # 打印初始共享数据
p = Process(target=func, args=(m_dic,))
p.start()
p.join()
print('主进程>>>>', m_dic) # 打印的是修改后的共享数据
'''
多进程同时获取数据, 修改后重新赋值, 可能同时拿到100, 减1后都把99赋值回去, 得到的数据不准确,数据不安全
可以通过加锁解决
'''
from multiprocessing import Process, Manager def func(m_dic):
m_dic['count'] -= 1 if __name__ == '__main__':
m = Manager()
m_dic = m.dict({'count': 100})
lst = []
for i in range(50):
p = Process(target=func, args=(m_dic,))
p.start()
lst.append(p) [p.join() for p in lst]
print('主进程>>>', m_dic)
# 加锁, 解决数据错乱问题
from multiprocessing import Process, Manager, Lock def func(m_dic, ml):
# with ml: 下面的缩进内容等同于 ml.acquire() ml.release() 之间的内容, 作用: 加锁
with ml:
m_dic['count'] -= 1 if __name__ == '__main__':
m = Manager()
ml = Lock()
m_dic = m.dict({'count': 100})
lst = []
for i in range(50):
p = Process(target=func, args=(m_dic, ml))
p.start()
lst.append(p) [p.join() for p in lst] print(m_dic)
进程池
什么是进程池?进程池的作用. 并行 并发 同步 异步 阻塞 非阻塞 互斥 死锁.
import time
from multiprocessing import Process, Pool def func(n):
time.sleep(1)
print(n) if __name__ == '__main__':
pool = Pool(4) # 设置进程数量, 如果不设置, 默认是CPU数量
pool.map(func, range(100)) # map自带join功能, 异步执行任务, 参数是可迭代对象
进程池比多进程效率高
import time
from multiprocessing import Process, Pool def func(n):
for i in range(100):
n += 1
print(n) if __name__ == '__main__':
pool_start_time = time.time()
pool = Pool()
pool.map(func, range(100))
pool_end_time = time.time()
pool_dif_time = pool_end_time - pool_start_time lst = []
p_s_time = time.time()
for i in range(100):
p = Process(target=func, args=(i,))
p.start()
lst.append(p)
[p.join() for p in lst]
p_e_time = time.time()
pd_time = p_e_time - p_s_time print('进程池执行时间>>>', pool_dif_time)
print('多进程执行时间>>>', pd_time)
进程池与多进程运行时间对比
import time
from multiprocessing import Process, Pool def func(i):
time.sleep(0.5)
print(i**2) if __name__ == '__main__':
pool = Pool(4)
pool_s_time = time.time()
pool.map(func, range(100))
pool_e_time = time.time()
pool_dif_time = pool_e_time - pool_s_time p_lst = []
p_s_time = time.time()
for i in range(100):
p = Process(target=func, args=(i,))
p.start()
p_lst.append(p)
[p.join() for p in p_lst]
p_e_time = time.time()
p_dif_time = p_e_time - p_s_time print('数据池执行时间:', pool_dif_time)
print('多进程运行时间:', p_dif_time)
这种情况下进程池比多进程 运行慢
进程池的同步方法: apply
import time
from multiprocessing import Process, Pool def fun(i):
time.sleep(0.5)
# print(i)
return i**2 if __name__ == '__main__':
p = Pool(4)
for i in range(10):
res = p.apply(fun, args=(i,)) # 同步执行的方法, 它会等待任务的返回结果(return)
print(res) # 打印的是fun的返回值(return) print('主进程结束') # 子进程都结束后打印
进程池的异步方法: apply_async # [ apply()方法的变体,它返回一个结果对象。]
import time
from multiprocessing import Process, Pool def fun(i):
time.sleep(0.5)
return i**2 if __name__ == '__main__':
p = Pool(4)
res_lst = []
for i in range(10):
res = p.apply_async(fun, args=(i,)) # 异步执行, res是对象multiprocessing.pool.ApplyResult object 主进程代码执行完毕不会等待子进程, 直接关闭主进程.
res_lst.append(res)
for i in res_lst:
print(i.get()) print('主进程结束') # 如果没有i.get()方法, 则主进程不会等待子进程执行完就会结束
get([timeout])
在产生结果时返回该结果。如果超时限制不是空, 而且结果没有在时限内返回, 则抛出多进程超时错误。 如果远程调用报出异常,那么get()方法将再次抛出这个异常。
# 如果不加close和join, 程序会直接随主进程结束运行,不会等待打印i. 加join后可以感知进程的运行 import time
from multiprocessing import Process, Pool def fun(i):
time.sleep(0.5)
print(i)
return i**2 if __name__ == '__main__':
p = Pool(4)
res_lst = []
for i in range(10):
res = p.apply_async(fun, args=(i,))
res_lst.append(res)
# print(res) # 异步执行, res是多个对象 <multiprocessing.pool.ApplyResult object at 0x000001B5BBD7C128> p.close() # 不是关闭进程池,而是不允许再有其他任务来使用进程池
p.join() # 这是感知进程池中任务的方法,等待进程池的任务全部执行完
for el in res_lst:
print('结果>>>', el.get())
# time.sleep(4) # 如果把close和join还有for循环都注释掉, 此处等待几秒也可以打印出i
print('主进程结束')
回调函数 callback
import os
from multiprocessing import Process, Pool
def func1(n):
print('func1', os.getpid())
return n*n def func2(nn):
print('func2', os.getpid())
print(nn) if __name__ == '__main__':
print('主进程:', os.getpid())
p = Pool(4)
p.apply_async(func1, args=(10,), callback=func2) #把func的返回结果传参给func2, func2 在主进程中运行 如果func1返回多个结果, 那么将以元组的形式传给func2
p.close()
p.join()
线程切换
#什么是线程:
#指的是一条流水线的工作过程,关键的一句话:一个进程内最少自带一个线程,其实进程根本不能执行,进程不是执行单位,是资源的单位,分配资源的单位
#线程才是执行单位
#进程:做手机屏幕的工作过程,刚才讲的
#我们的py文件在执行的时候,如果你站在资源单位的角度来看,我们称为一个主进程,如果站在代码执行的角度来看,它叫做主线程,只是一种形象的说法,其实整个代码的执行过程成为线程,也就是干这个活儿的本身称为线程,但是我们后面学习的时候,我们就称为线程去执行某个任务,其实那某个任务的执行过程称为一个线程,一条流水线的执行过程为线程 #进程vs线程
#1 同一个进程内的多个线程是共享该进程的资源的,不同进程内的线程资源肯定是隔离的
#2 创建线程的开销比创建进程的开销要小的多 #并发三个任务:1启动三个进程:因为每个进程中有一个线程,但是我一个进程中开启三个线程就够了
#同一个程序中的三个任务需要执行,你是用三个进程好 ,还是三个线程好?
#例子:
# pycharm 三个任务:键盘输入 屏幕输出 自动保存到硬盘
#如果三个任务是同步的话,你键盘输入的时候,屏幕看不到
#咱们的pycharm是不是一边输入你边看啊,就是将串行变为了三个并发的任务
#解决方案:三个进程或者三个线程,哪个方案可行。如果是三个进程,进程的资源是不是隔离的并且开销大,最致命的就是资源隔离,但是用户输入的数据还要给另外一个进程发送过去,进程之间能直接给数据吗?你是不是copy一份给他或者通信啊,但是数据是同一份,我们有必要搞多个进程吗,线程是不是共享资源的,我们是不是可以使用多线程来搞,你线程1输入的数据,线程2能不能看到,你以后的场景还是应用多线程多,而且起线程我们说是不是很快啊,占用资源也小,还能共享同一个进程的资源,不需要将数据来回的copy!
什么是线程
线程的创建方法一:
# 线程和进程很像, 一个进程中至少有一个线程, 进程是资源层面的, 线程负责实际的操作 import time
from threading import Thread def func(n):
time.sleep(1) # 子线程运行地太快了, 如果不加time.sleep,会在打印主线程之前跑完
print(123) if __name__ == '__main__':
t = Thread(target=func, args=(1,))
t.start()
t.join() # 等待子线程跑完再执行主线程下面的内容
print('主线程')
线程的创建方法二:
from threading import Thread class MyThread(Thread):
def __init__(self, n):
super().__init__()
self.n = n
def run(self):
print('换汤不换药')
print('self.n>>>', self.n) if __name__ == '__main__':
t = MyThread('你好')
t.start()
t.join()
print('主线程结束')
Day035--Python--管道, Manager, 进程池, 线程切换的更多相关文章
- python并发编程-进程池线程池-协程-I/O模型-04
目录 进程池线程池的使用***** 进程池/线程池的创建和提交回调 验证复用池子里的线程或进程 异步回调机制 通过闭包给回调函数添加额外参数(扩展) 协程*** 概念回顾(协程这里再理一下) 如何实现 ...
- day 32 管道,信号量,进程池,线程的创建
1.管道(了解) Pipe(): 在进程之间建立一条通道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道. ...
- Python中的进程池与线程池(包含代码)
Python中的进程池与线程池 引入进程池与线程池 使用ProcessPoolExecutor进程池,使用ThreadPoolExecutor 使用shutdown 使用submit同步调用 使用su ...
- Python并发编程05 /死锁现象、递归锁、信号量、GIL锁、计算密集型/IO密集型效率验证、进程池/线程池
Python并发编程05 /死锁现象.递归锁.信号量.GIL锁.计算密集型/IO密集型效率验证.进程池/线程池 目录 Python并发编程05 /死锁现象.递归锁.信号量.GIL锁.计算密集型/IO密 ...
- python中的进程、线程(threading、multiprocessing、Queue、subprocess)
Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就比别人NB. 我们先了解一下什么是进程和线程. 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CP ...
- concurrent.futures模块(进程池/线程池)
需要注意一下不能无限的开进程,不能无限的开线程最常用的就是开进程池,开线程池.其中回调函数非常重要回调函数其实可以作为一种编程思想,谁好了谁就去掉 只要你用并发,就会有锁的问题,但是你不能一直去自己加 ...
- concurrent.futures模块(进程池&线程池)
1.线程池的概念 由于python中的GIL导致每个进程一次只能运行一个线程,在I/O密集型的操作中可以开启多线程,但是在使用多线程处理任务时候,不是线程越多越好,因为在线程切换的时候,需要切换上下文 ...
- python中的进程池
1.进程池的概念 python中,进程池内部会维护一个进程序列.当需要时,程序会去进程池中获取一个进程. 如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止. 2.进程池 ...
- 13 并发编程-(线程)-异步调用与回调机制&进程池线程池小练习
#提交任务的两种方式 #1.同步调用:提交完任务后,就在原地等待任务执行完毕,拿到结果,再执行下一行代码,导致程序是串行执行 一.提交任务的两种方式 1.同步调用:提交任务后,就在原地等待任务完毕,拿 ...
随机推荐
- Javassist之使用字节码在运行时生成新的类 01
介绍 Javassist是一个开源的分析.编辑和创建Java字节码的类库.是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的.它已加入了开放源代码JBoss 应用 ...
- How to enable usb on vbox
Device-->Install Guest Addition Shared Folders Settings-->Advanced-->Shared Clipboard--> ...
- 学习 Spring (一) Spring 介绍
Spring入门篇 学习笔记 Spring 是什么 Spring 是一个轻量级的 IoC (控制反转)和 AOP (面向切面)的容器框架 框架与类库的区别 框架一般是封装了逻辑.高内聚的,类库则是松散 ...
- 使用 Travis CI 自动部署 Hexo 站点至 GitHub Pages
Hexo 与 GitHub Pages 安装配置请参考:Hexo 与 GitHub Pages 本文源码与生成的静态文件在同一项目下,源码在 source 分支,静态文件在 master 分支 新增 ...
- centOS7 下配置和启动maria数据库
从最新版本的linux系统开始,默认的是 Mariadb而不是mysql! 使用系统自带的repos安装很简单: yum install mariadb mariadb-server systemct ...
- [离散时间信号处理学习笔记] 7. z变换
z变换及其收敛域 回顾前面的文章,序列$x[n]$的傅里叶变换(实际上是DTFT,由于本书把它叫做序列的傅里叶变换,因此这里以及后面的文章也统一称DTFT为傅里叶变换)被定义为 $X(e^{j\ome ...
- Codeforces Round #437 Div. 1
A:显然构造一组只包含1和2面值的数据即可. #include<iostream> #include<cstdio> #include<cmath> #includ ...
- CodeForces512C-Pluses everywhere-模拟/数学/排列组合模板
经过研究可以发现,每一位的贡献是C(n-2,k-1)+C(n-3,k-1)...C(k-1,k-1) 同时还要注意加号全部在左边的情况. 这里还用了O(n)预处理O(1)组合数的模板.//妙啊..妙. ...
- Codeforces1037F Maximum Reduction 【单调栈】
题目分析: 没啥好说的,会单调栈就会做. 代码: #include<bits/stdc++.h> using namespace std; ; ; int n,k; int pre[max ...
- BZOJ1835 [ZJOI2010] 基站选址 【动态规划】【线段树】
题目分析: 首先想一个DP方程,令f[m][n]表示当前在前n个村庄选了m个基站,且第m个基站放在n处的最小值,转移可以枚举上一个放基站的村庄,然后计算两个村庄之间的代价. 仔细思考两个基站之间村庄的 ...