今天给大家说说多进程!
梳理接下来的知识点:
1. 多进程的介绍
2. 多进程的特点
3. 多进程的简单使用
4. 进程直接数据交互
5. 进程直接的数据通信
6. 进程之间的内存共享
7. 进程同步 # 没写
8. 进程池 # 没写
1.多进程的介绍
在Python中,基本都知道多线程是 假
的多线程.它是在一个核上跑,并没有达到多核运算。
而且由于GIL这把超级大锁的原因,发生了很多让人无语的事情。
而且当线程多的时候,效率还不如不串行的速度快。
所以,很多人都只拿Python的多线程做 IO 处理,因为不消耗CPU嘛,到了使用CPU进行运算的时候,大家直接转用多进程。
在Python中,多线程是假的,但是多进程是真的。
这里我们科普一下进程和线程的一些问题。
- 每一个进程中必须有一个或一个以上的线程。
- 父进程和子进程,通常只是名义上的 ·父子关系·
- 启动一个进程,就会启动一个线程,所以,当被问到启动线程快还是启动进程快,答案是线程快。
- 当启动进程数量大于CPU核数,就会进行排队,比如是8核,只能同时跑8个进程,如果超出,就会被安排在 后面,当8个里面有一个结束,就会马上从排在后面的进程中放进去一个。
还有很多啦。。自己去Google啦。
2.多进程的特点
- 通常会有一个父进程
- 没有进程间内存共享那么方便
- 存在独立的PID
- 动态性
- .......自己Google...
3.进程的简单使用
import multiprocessing
import os
import time
"""
os.getppid() 获取当前进程的父进程的PID
os.getpid() 获取当前进程的PID
"""
# 定义
def run():
time.sleep(3)
print('父进程的PID:',os.getppid())
print("当前进程的PID:",os.getpid())
# 继承多进程
class MyProcess(multiprocessing.Process):
def __init__(self,func):
super(MyProcess,self).__init__()
self.func = func
def run(self):
self.func()
if __name__ == '__main__':
print("主进程的父进程PID:",os.getppid()) # 主进程的父进程的PID
print("主进程的PID:",os.getpid()) # 主进程的 PID
print("_"*20 + "\n")
Process = MyProcess(run,"XeanYu") # 实例化一个多进程对象
Process.start() # 执行多进程
这样就是简单使用多进程的实例。
4.进程之间的数据交互
from multiprocessing import Process,Queue
"""
Queue 实现进程间的 `数据交互`,中间使用 `序列化`
"""
# 简单的小钩子,用于快速将数据做成一个列表返回。
def get_all(queue):
q = []
if hasattr(queue,'get' and 'empty'):
while True:
if queue.empty(): # 判断 队列 `q` 是否为空。 null => True or not null => False
return q # 跳出循环,返回结果.
q.append(queue.get())
else:
raise TypeError("Please input Queue Object!")
"""
使用 multiprocessing 中的 Queue(队列)
"""
def run(q,name):
q.put(name)
if __name__ == '__main__':
li = []
q = Queue()
process1 = Process(target=run,args=(q,'XeanYu'))
process2 = Process(target=run,args=(q,'XY'))
process1.start()
process2.start()
process1.join()
process2.join()
li = get_all(q)
print(li)
这样看起来很像内存共享,但实际是。
举栗子:
- 进程A 将数据序列化后,进程B进行反序列化,就实现了数据交互,这不是内存共享!
5.进程之间的数据通讯
from multiprocessing import Process,Pipe
def Son(pipe):
pipe.send("Hello Father") # 向父亲发送 `Hello Father`
print("From Father:" + pipe.recv()) # 接收父亲发过来的 `Hi my son!`
def Father(pipe):
print("From Son:" + pipe.recv()) # 接收儿子的发来的 `Hello Father`
pipe.send("Hi my son!") # 发送给儿子,说 `Hi my son!`
if __name__ == '__main__':
father,son = Pipe() # 实例化一个父亲和儿子
f = Process(target=Father,args=(father,)) # 父亲的进程
s = Process(target=Son,args=(son,)) # 儿子的进程
f.start() # 启动
s.start() # 启动
""""
结果:
>>> From Son:Hello Father
>>> From Father:Hi my son!
""""
我们使用Pipe() 函数,在进程Son和进程Father直间建立一个管道,进行进程直接的通信。使用方法很像是Socekt的操作。
其中,father,son = Pipe() 是返回两个通信的双方,然后其中的father,son都有一个send()发送,recv()接收的方法。
6. 进程之间的内存共享.
from multiprocessing import Process,Manager
def run(d,i):
d.append(i)
if __name__ == '__main__':
manager = Manager() # 实例化
d = manager.list() # 定义一个list类型,这个list是进程内存共享的list
p_list = [] # 进程表
for i in range(5): # 将每个 i 传入参数
p = Process(target=run,args=(d,i))
p.start()
p_list.append(p)
for i in p_list:
i.join()
print(d) # 查看d,有没有被多个进程操作一个内存地址。
补充:Manager() 支持很多类型的数据。列出一些: list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array.
中途总结:
from multiprocessing import Process,Manager,Pipe
from random import randint
"""
知识点要求:
1.多进程使用
2.多进程之间的通信
3.多进程之间的内存共享
4.使用自己定义类,并且继承多线程
"""
class MeProcess(Process): # 继承多线程
def __init__(self,func,args=()):
super(MeProcess,self).__init__(target=func,args=args) # 为了省事,就直接这样写了,没写 run(self)
def send(a,random_int):
a.send(random_int) # 发送
def add(inter,b):
inter["int"] = inter.get('int') + int(b.recv())
print(inter.get('int'))
if __name__ == '__main__':
manager = Manager()
A,B = Pipe()
inter = manager.dict()
inter['int'] = 0
p_list = []
for i in range(10):
Pa = MeProcess(func=send,args=(A,randint(10,100)))
Pb = MeProcess(func=add,args=(inter,B))
Pa.start()
Pb.start()
p_list.append(Pa)
p_list.append(Pb)
for i in p_list:
i.join()
print(inter.get('int'))
7.进程同步
等更新
8.进程池
等更新
有什么问题请加微信交流...