进程(Process):拥有自己独立的堆和栈,既不共享堆,也不共享栈,进程由操作系统调度;进程切换需要的资源最大,效率低。
进程的优缺点
进程的优点:
- 可以使用计算机多核,进程任务的并行执行,提高执行效率
- 运行不受其他进程的影响,创建方便
- 空间独立,数据安全
进程的缺点:
- 进程的创建和删除消耗的系统资源较多
进程的创建方式(方法模式)
Python的标准库提供模块:multiprocessing
进程的创建方式有两种方式:
- 方法包装
- 类包装
# encoding=utf-8
# 方法包装-多进程实现
from multiprocessing import Process
import os
from time import sleep
def func1(name):
print("当前进程ID:",os.getpid())
print("父进程ID:",os.getppid())
print(f"Process:{name},start")
sleep(2)
print(f"Process:{name},end")
if __name__ == '__main__':
print("当前进程ID:",os.getpid())
# 创建进程
p1 = Process(target=func1,args=("p1",))
p2 = Process(target=func1,args=("p2",))
p1.start()
p2.start()
"""
当前进程ID: 9588
当前进程ID: 8004
当前进程ID: 4476
父进程ID: 9588
Process:p1,start
父进程ID: 9588
Process:p2,start
Process:p1,end
Process:p2,end
Process finished with exit code 0
"""
进程的创建方式(继承Process类)
和使用Thread类创建子线程的方式非常类似,使用Process类创建实例化对象,其本质是调用该类的构造方法创建新进程。Process类的构造方法格式:
def __init__(self,group=None,target=None,name=None,args=(),kwargs={})
group:该参数未进行实现,不需要传参
target:为新建进程指定执行任务,也就是指定一个函数
name:为新建进程设置名称
args:为target参数指定的参数传递非关键字参数
kwargs:为target参数指定的参数传递关键字参数
# 类模式创建进程
from multiprocessing import Process
from time import sleep
class MyProcess(Process):
def __init__(self,name):
Process.__init__(self)
self.name = name
def run(self):
print(f"Process:{self.name},start")
sleep(3)
print(f"Process:{self.name},end")
if __name__ == '__main__':
# 创建进程
p1 = MyProcess("p1")
p2 = MyProcess("p2")
p1.start()
p2.start()
"""
Process:p1,start
Process:p2,start
Process:p1,end
Process:p2,end
Process finished with exit code 0
"""
Queue实现进程间通信
实现线程间的通信使用Queue模块中的Queue类,实现进程间的通信需要multiprocessing模块中的Queue类。简单理解Queue实现进程通信的方式就是使用了操作系统给开辟的一个队列空间,各个进程可以把数据放到该队列中,当然也可以从队列中把自己需要的信息取走。
# 使用Queue实现进程间通信的经典代码
from multiprocessing import Process,Queue
from time import sleep
class MyProcess(Process):
def __init__(self,name,mq):
Process.__init__(self)
self.name = name
self.mq = mq
def run(self):
print("Process:{},start".format(self.name))
print(f"get Data:{self.mq.get()}")
sleep(2)
self.mq.put(f"new_data:{self.name}")
print(f"Process:{self.name},end")
if __name__ == '__main__':
# 创建进程列表
p_list = []
mq = Queue()
mq.put("1")
mq.put("2")
mq.put("3")
# 循环创建进程
for i in range(3):
p = MyProcess("p{}".format(i),mq)
p_list.append(p)
for p in p_list:
p.start()
for p in p_list:
p.join()
print(mq.get())
print(mq.get())
print(mq.get())
"""
Process:p0,start
get Data:1
Process:p2,start
get Data:2
Process:p1,start
get Data:3
Process:p2,endProcess:p1,endProcess:p0,end
new_data:p2
new_data:p1
new_data:p0
Process finished with exit code 0
"""
Pipe实现进程间的通信
Pipe方法返回conn1、conn2代表一个管道的两端。Pipe方法有duplex参数,如果duplex参数为True(默认值),那么这个参数是全双工模式,也就是说conn1和conn2均可收发。若duplex为False,conn1只负责接收消息,conn2只负责发送消息。send和recv方法分别是发送和接收消息的方法。在全双工模式下,可以调用conn1.send发送消息,conn1.recv接收消息。如果没有消息可接收,recv方法会一致阻塞。如果管道已经被关闭,那么recv方法会抛出EOFError。
#encoding=utf-8
import multiprocessing
from time import sleep
def func1(conn1):
sub_info = "Hello"
print(f"进程1{multiprocessing.current_process().pid}发送数据:{sub_info}")
sleep(1)
conn1.send(sub_info)
print(f"来自进程2的:{conn1.recv()}")
sleep(1)
def func2(conn2):
sub_info = "你好"
print(f"进程2{multiprocessing.current_process().pid}发送数据:{sub_info}")
sleep(1)
conn2.send(sub_info)
print(f"来自进程1的:{conn2.recv()}")
sleep(1)
if __name__ == '__main__':
# 创建管道
conn1,conn2 = multiprocessing.Pipe()
# 创建子进程
process1 = multiprocessing.Process(target=func1,args=(conn1,))
process2 = multiprocessing.Process(target=func2,args=(conn2,))
# 启动子进程
process1.start()
process2.start()
"""
进程120024发送数据:Hello
进程26072发送数据:你好
来自进程1的:Hello
来自进程2的:你好
Process finished with exit code 0
"""
Manager管理器实现进程通信
管理器提供了一种创建共享数据的方法,从而可以在不同进程中共享。
# encoding=utf-8
from multiprocessing import Process,current_process
from multiprocessing import Manager
def func(name,m_list,m_dict):
m_dict['name'] = "Saber"
m_list.append("你好")
if __name__ == '__main__':
with Manager() as mgr:
m_list = mgr.list()
m_dict = mgr.dict()
m_list.append("Hello")
# 两个进程不能直接互相使用对象,需要互相传递
p1 = Process(target=func,args=("p1",m_list,m_dict))
p1.start()
# 等待p1进程结束,主进程继续执行
p1.join()
print(f"主进程:{m_list}")
print(f"主进程:{m_dict}")
"""
主进程:['Hello', '你好']
主进程:{'name': 'Saber'}
Process finished with exit code 0
"""
进程池管理进程的两种典型案例
Python提供了进程池管理多个进程的方式。进程池可以提供指定数量的进程给客户使用,即当有新的请求提交到进程池中时,如果池未满,则会创建一个新的进程用来执行该请求;如果池中进程数已经达到规定最大值,那么该请求就会等待,只要池中进程空闲下来,该请求就能得到执行。
进程池的优点:
- 提高效率,节省开辟进程和开辟内存空间的时间,以及销毁进程的时间
- 节省内存空间
类/方法 | 功能 | 参数 |
---|---|---|
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() | 回收进程池 | |
pool.map(func,iter) | 类似于python的map函数,将要做的事件放入进程池 | func 要执行的函数 iter 迭代对象 |
# encoding=utf-8
# 进程池使用案例
from multiprocessing import Pool
import os
from time import sleep
def func1(name):
print(f"当前进程的ID:{os.getpid()},{name}")
sleep(2)
return name
def func2(args):
print(args)
if __name__ == '__main__':
pool = Pool(5)
pool.apply_async(func=func1,args=("竹筒饭1",),callback=func2)
pool.apply_async(func=func1,args=("竹筒饭2",),callback=func2)
pool.apply_async(func=func1,args=("竹筒饭3",),callback=func2)
pool.apply_async(func=func1,args=("竹筒饭4",))
pool.apply_async(func=func1,args=("竹筒饭5",))
pool.apply_async(func=func1,args=("竹筒饭6",))
pool.apply_async(func=func1,args=("竹筒饭7",))
pool.apply_async(func=func1,args=("竹筒饭8",))
pool.close()
pool.join()
"""
当前进程的ID:19004,竹筒饭1
当前进程的ID:3888,竹筒饭2
当前进程的ID:7188,竹筒饭3
当前进程的ID:15168,竹筒饭4
当前进程的ID:3548,竹筒饭5
当前进程的ID:3548,竹筒饭6
当前进程的ID:3888,竹筒饭7
当前进程的ID:19004,竹筒饭8
竹筒饭1
竹筒饭2
竹筒饭3
Process finished with exit code 0
"""
# encoding=utf-8
# 使用with管理进程池
from multiprocessing import Pool
import os
from time import sleep
def func1(name):
print(f"当前进程ID:{os.getpid()},{name}")
sleep(2)
return name
if __name__ == '__main__':
with Pool(5) as pool:
args = pool.map(func1,("竹筒饭1","竹筒饭2","竹筒饭3","竹筒饭4",
"竹筒饭5","竹筒饭6","竹筒饭7","竹筒饭8"))
for a in args:
print(a)
"""
当前进程ID:4328,竹筒饭1
当前进程ID:9960,竹筒饭2
当前进程ID:10132,竹筒饭3
当前进程ID:9452,竹筒饭4
当前进程ID:6256,竹筒饭5
当前进程ID:9960,竹筒饭6
当前进程ID:4328,竹筒饭7
当前进程ID:10132,竹筒饭8
竹筒饭1
竹筒饭2
竹筒饭3
竹筒饭4
竹筒饭5
竹筒饭6
竹筒饭7
竹筒饭8
Process finished with exit code 0
"""