复习
孤儿进程和僵尸进程?
如何避免僵尸进程产生?
multiprocessing 创建进程
Process start join
multiprocessing 模块创建进程
- 需要将要做的事件进行封装成函数
- 使用multiprocessing提供的类Process创建进程对象
- 通过对象和Process的初始化函数对进程进行设置以
及绑定要执行的事件 - 启动进程,会自动的执行函数代表的事件
- 完成进程的回收
创建进程对象
Process()
功能:创建进程对象
参数:name : 给创建的进程对象起一个名字
默认为Process-1
target : 绑定的函数
args :元组 用来给 target函数传参按位置传参
kwargs : 字典 按照键值传参
p.start()
功能:启动进程 进程被创建,自动运行对应函数
p.join([timeout])
功能 : 阻塞等待对应子进程的退出,回收子进程
参数 : 超时时间
- 如果不使用join 则子进程会成为僵尸进程
- 在使用multiprocessing创建进程中,一般父进程功能
就是创建子进程等待回收,不做过多其他事情 - 使用multiprocessing创建子进程,同样子进程复制父
进程空间,之后有自己独立的执行空间,互不干扰
import multiprocessing as mp
from time import sleep
a = 1
def fun():
global a
a = 1000
sleep(3)
print(“子进程事件”)
#创建进程对象
p = mp.Process(target = fun)
#启动进程
p.start()
sleep(2)
print(“这是父进程”)
#回收进程
p.join()
print("a = ",a)
from multiprocessing import Process
from time import sleep
import os
def th1():
sleep(3)
print(“吃饭”)
print(os.getppid(),"----",os.getpid())
def th2():
sleep(2)
print(“睡觉”)
print(os.getppid(),"----",os.getpid())
def th3():
sleep(4)
print(“打豆豆”)
print(os.getppid(),"----",os.getpid())
thing = [th1,th2,th3]
process = []
for th in thing:
p = Process(target = th)
process.append§ #将进程对象保存
p.start()
#回收进程
for i in process:
i.join()
#01_process3.py
from multiprocessing import Process
from time import sleep
#带参数的事件函数
def worker(sec,name):
for i in range(3):
sleep(sec)
print(“I’m %s”%name)
print(“I’m working…”)
#创建进程,通过args,kwargs进行参数传递
p = Process(name = “Worker”,target = worker,
args = (2,),kwargs = {‘name’:“Alex”})
p.start()
#进程状态
print(“is alive :”,p.is_alive())
#进程名称
print(“Process name:”,p.name)
#进程pid
print(“Process PID:”,p.pid)
#回收进程
p.join(4) #4表示超时时间
print("===============")
is alive : True
Process name: Worker
Process PID: 5162
I’m Alex
I’m working…
I’m Alex
I’m working…
===============
I’m Alex
I’m working…
进程对象属性
p.join() 回收进程
p.start() 启动进程
p.is_alive() 判断进程是否在生命周期状态,在生命周
期中返回True 否则返回False
p.name 进程名称 默认为Process-1 如果起名字则为自
己取的名称
p.pid 进程的PID号
p.daemon
默认为False 主进程退出不会影响子进程
如果设置为 True 则主进程退出时会让所有子进程都退出
- 该属性的设置必须在start() 前
- 该属性一般不用和join同时出现
02_process4.py
from multiprocessing import Process
from time import sleep,ctime
def tm1():
while True:
sleep(2)
print(ctime())
p = Process(target = tm1)
p.daemon = True
p.start()
sleep(5)
print(“main process over”)
Mon Aug 6 14:56:12 2018
Mon Aug 6 14:56:14 2018
main process over
创建自定义进程类
1.继承Process类
2.运行Process类的__init__ 以获取父类属性
3.重写run方法,在通过自定类生成对象后,调用
start()会自动执行这个方法
03_clock.py
from multiprocessing import Process
import time
class ClockProcess(Process):
def init(self, value):
#使用父类的init保证同时拥有父类的属性
super(ClockProcess, self).init()
# Process.init(self)
self.value = value
#自定义的类中,重写run方法
def run(self):
for i in range(5):
print("The time is {}".\
format(time.ctime()))
time.sleep(self.value)
#用自己的类创建进程对象
p = ClockProcess(2)
会自动执行run方法
p.start()
p.join()
The time is Mon Aug 6 15:14:43 2018
The time is Mon Aug 6 15:14:45 2018
The time is Mon Aug 6 15:14:47 2018
The time is Mon Aug 6 15:14:49 2018
The time is Mon Aug 6 15:14:51 2018
多进程
优点 : 并行执行多个任务,提高效率
创建方便
运行独立,不受其他进程影响
数据安全
缺点 :在进程的创建和删除过程中消耗计算机资源较多
进程池技术
产生原因 :如果有大量的任务需要多进程完成,则可能
需要频繁的创建和删除进程,给计算机带来
较多的消耗。
使用 :大量可以短时间完成的任务需要多进程操作的时
候比较适用于进程池
使用方法:
- 创建进程池,在池内放入适当的进程
- 将事件加入进程池队列
- 事件不断运行,所有事件运行完成
- 关闭进程池,回收进程
from multiprocessing import Pool
Pool(processes)
功能 : 创建进程池对象
参数 : processes 表示进程池中有多少进程
对象 : 进程池对象
pool.apply_async(func,args,kwds)
功能 : 将事件放入进程池等待执行
参数 : func 要放入进程池的事件函数
args 给func函数以元组传参
kwds 给func函数以字典传参
返回值 : 返回一个事件对象
通过get()方法可以获取事件函数的返回值
pool.apply(func,args,kwds)
功能 : 将事件放入进程池等待执行
参数 : func 要放入进程池的事件函数
args 给func函数以元组传参
kwds 给func函数以字典传参
pool.close()
功能 : 关闭进程池 不能再添加新的事件
pool.join()
功能 : 回收进程池
04_pool.py
from multiprocessing import Pool
from time import sleep,ctime
def worker(msg):
sleep(2)
print(msg)
return ctime()
#创建进程池对象
pool = Pool(processes = 4)
result = []
for i in range(10):
msg = “hello %d”%i
#将事件加入到进程池
pool.apply(func = worker,args = (msg,))
# r = pool.apply_async(func = worker,args = (msg,))
# result.append®
#关闭进程池
pool.close()
#回收进程
pool.join()
#打印各个事件函数返回值
for i in result:
print(i.get())
# hello 0
# hello 2
# hello 3
# hello 1
# hello 5
# hello 6
# hello 4
# hello 7
# hello 9
# hello 8
# Mon Aug 6 16:00:22 2018
# Mon Aug 6 16:00:22 2018
# Mon Aug 6 16:00:22 2018
# Mon Aug 6 16:00:22 2018
# Mon Aug 6 16:00:24 2018
# Mon Aug 6 16:00:24 2018
# Mon Aug 6 16:00:24 2018
# Mon Aug 6 16:00:24 2018
# Mon Aug 6 16:00:26 2018
# Mon Aug 6 16:00:26 2018
hello 0
hello 1
hello 2
hello 3
hello 4
hello 5
hello 6
hello 7
hello 8
hello 9
pool.map(func,iter)
功能 : 将要完成的事件放入到进程池
参数 : func 要完成的事件函数
iter 要给func传递的参数的迭代器
返回值 : 返回事件函数的返回值列表
05_pool_map.py
from multiprocessing import Pool
import time
def fun(n):
time.sleep(1)
print(“执行 pool map 事件”,n)
return n * n
pool = Pool(4)
#map放入6个事件到进程池
r = pool.map(fun,range(6))
print(“返回值列表:”,r)
pool.close()
pool.join()
执行 pool map 事件 3
执行 pool map 事件 1
执行 pool map 事件 2
执行 pool map 事件 0
执行 pool map 事件 4
执行 pool map 事件 5
返回值列表: [0, 1, 4, 9, 16, 25]
练习 : 使用multiprocessing 创建两个进程,分别复制
一个文件的上半部分和下半部分到另外一个新的
文件中
cookie : os.path.getsize(path) 获取文件大小
#06_copy_file.py
import os
from multiprocessing import Process
#获取文件大小
size = os.path.getsize("./file.jpg")
#两个子进程操作同一个文件流会产生错乱
f = open(‘file.jpg’,‘rb’)
#复制前半部分
def copy1(filename):
f = open(filename,‘rb’)
n = size // 2
fw = open(‘copy1.jpg’,‘wb’)
while True:
if n < 128:
data = f.read(n)
fw.write(data)
break
data = f.read(128)
fw.write(data)
n -= 128
f.close()
fw.close()
#复制下半部分
def copy2(filename):
f = open(filename,‘rb’)
fw = open(‘copy2.jpg’,‘wb’)
f.seek(size // 2,0)
while True:
data = f.read(128)
if not data:
break
fw.write(data)
fw.close()
f.close()
p1 = Process(target = copy1,args = (“file.jpg”,))
p2 = Process(target = copy2,args = (“file.jpg”,))
p1.start()
p2.start()
p1.join()
p2.join()
进程间通信
进程间由于空间独立,资源互相无法直接获取,此时在不同
的进程间传递数据就需要专门的进程间通信方法。
和磁盘交互 : 使用中间文件 但是不安全,速度慢
进程间通信方法 (IPC)
管道 消息队列 共享内存 信号 信号量 套接字
管道通信 Pipe
在内存中开辟一块空间,形成管道结构,管道对多个进程可见,
进程可以通过对管道的读写操作进行通信
multiprocessing —》 Pipe
fd1,fd2 = Pipe(duplex = True)
功能 : 创建一个管道
参数 : 默认表示管道为双向管道
如果设置为False 则表示单向管道
返回值 : 返回两个管道流对象,表示管道两端
如果是双向管道,则都可以读写
如果是单向管道,则fd1只读 fd2只写
fd1.recv()
功能 : 从管道内读信息
返回值:读到的内容
- 当管道内无内容时会阻塞
fd2.send(data)
功能 : 向管道写入内容
参数 : 要写的内容
- 可以发送几乎Python的任意数据类型
#07_pipe.py
from multiprocessing import Process,Pipe
import os,time
#创建管道
fd1,fd2 = Pipe()
def fun(name):
time.sleep(3)
#向管道内写入内容
fd1.send(“hello” + str(name))
jobs = []
for i in range(5):
p = Process(target = fun,args = (i,))
jobs.append§
p.start()
for i in range(5):
#读取管道内消息
data = fd2.recv()
print(data)
for i in jobs:
i.join()
hello0
hello1
hello2
hello4
hello3
消息队列
队列 : 先进先出
在内存中开辟队列结构空间,对多个进程可见。多个进程向队列中存入消息,
取出消息,完成进程间通信。
创建队列
q = Queue(maxsize = 0)
功能 : 创建队列
参数 : maxsize 默认表示根据系统分配空间存储消息
如果传入一个正整数则表示最多存入消息数量
返回值 : 队列对象
q.put(data,[block,timeout])
功能 : 向队列中存入消息
参数 : data 存入的数据 (支持Python数据类型)
block 默认为True表示 当队列满时阻塞
设置为False 表示非阻塞
timeout 当block为Ture时表示超时时间
data = q.get([block,timeout])
功能 : 从队列获取消息
参数 : block 默认为True表示 当队列空时阻塞
设置为False 表示非阻塞
timeout 当block为Ture时表示超时时间
返回值:返回获取到的消息
q.full() 判断队列是否为满
q.empty() 判断队列是否为空
q.qsize() 获取队列中消息数量
q.close() 关闭队列
08_queue1.py
from multiprocessing import Queue
from time import sleep
#创建队列
q = Queue(3)
q.put(1)
sleep(1)
print(q.empty())
print(q.full())
q.put(2)
q.put(3)
print(q.full())
# q.put(4,True,3)
print(q.get())
print(q.qsize()) #查看消息数量
print(q.empty())
q.close()
08_queue2.py
from multiprocessing import Process,Queue
import time
#创建消息队列
q = Queue()
def fun1():
time.sleep(1)
q.put({‘a’:1,‘b’:2})
def fun2():
time.sleep(2)
print(“收到消息:”,q.get())
p1 = Process(target = fun1)
p2 = Process(target = fun2)
p1.start()
p2.start()
p1.join()
p2.join()
收到消息: {‘b’: 2, ‘a’: 1}
共享内存
在内存中开辟一段空间,存储数据,对多个进程可见。
每次写入共享内存的数据会覆盖之前的内容
from multiprocessing import Value,Array
obj = Value(ctype,obj)
功能 : 开辟共享内存空间
参数 : ctype 字符串 要转变的c的类型code(对照 ctype表)
obj 共享内存初始值
返回共享内存对象
obj.value 表示共享内存中的值。对其修改或者使用即
为使用共享内存中的值
obj = Array(ctype,obj)
功能 : 开辟共享内存空间
参数 : ctype 要转换的类型
obj 要存入共享内存的数据
列表 将列表存入共享内存 要求类型一致
整数 在共享内存中开辟几个单元的空间
返回共享内存对象
管道 消息队列 共享内存
开辟空间 内存 内存 内存
读写方式 两端读写 先进先出 操作覆盖内存
双向/单向
效率 一般 一般 较快
应用 多用于父子 广泛灵活 复杂,需要
进程 互斥机制
10_value.py
from multiprocessing import Process,Value
import time
import random
#共享内存对象初始存放2000
money = Value(‘i’,2000)
#存钱进程
def deposite():
for i in range(100):
time.sleep(0.05)
#对value属性操作实际就是在操作共享内存
money.value += random.randint(1,200)
#取钱
def withdraw():
for i in range(100):
time.sleep(0.04)
#对value属性操作实际就是在操作共享内存
money.value -= random.randint(1,200)
d = Process(target = deposite)
w = Process(target = withdraw)
d.start()
w.start()
d.join()
w.join()
print(money.value)
11_array.py
from multiprocessing import Process,Array
import time
#创建共享内存 列表是共享内存初始值
shm = Array(‘i’,[1,2,3,4,5])
#表示共享内存中开辟5个整形空间
shm = Array(‘i’,5)
def fun():
for i in shm:
print(i)
#修改共享内存内容
shm[3] = 1000
p = Process(target = fun)
p.start()
p.join()
for i in shm:
print(i)
作业 : 熟练掌握进程间通信方法
自己实现进程池的使用,知道进程池特性