冯诺依曼体系
单进程
在Python中,执行一个Python脚本,就会创建一个进程。
可以用os.getpid()
获得进程的PID
多进程
import os
import time
from multiprocessing import Process # 导入多进程的包
def cloudmusic(): # 模拟网易云音乐的进程
for i in range(5):
print(f"子进程{os.getpid()}正则播放 无人之岛...")
time.sleep(1)
def lol(n): # 模拟lol进程(带参数的进程)
for i in range(n):
print(f"子进程{os.getpid()}游戏中,请勿打扰...")
time.sleep(1)
def process_test():
start_time = time.time()
p1 = Process(target=cloudmusic) # 创建网易云音乐的进程
# 带参数的进程使用args传参必须是元祖形式,参数顺序与方法形参顺序一致
p2 = Process(target=lol,args=(8,)) # 创建lol的进程
# 以kwargs传参
#p2 = Process(target=lol,kwargs={"name":8})
p1.daemon = True
p2.daemon = False
p1.start() # 启动子进程p1
p2.start() # 启动子进程p2
p1.join()
p2.join()
end_time = time.time()
print(end_time - start_time) # 这是主进程中最后1行代码,运行完,主进程结束
# 执行到创建子进程代码时,会自动拷贝其余代码来运行,在Linux和mac中,不会拷贝创建进程的代码
# 在Windows中,必须将创建子进程的代码放在main中,否则会递归无限制创建子进程,会报错
if __name__ == '__main__':
process_test()
主进程和子进程
主进程:一般写的代码都是主进程。
子进程:在主进程通过Process类创建的进程对象就是子进程。
daemon进程守护
设置daemon = True
,主进程执行完,不会等待该进程,同时所有daemon值为True的子线程将随主线程一起结束,而不论是否运行完成。
设置daemon = False
(不设置时默认为False),主进程执行完,会等待该进程执行完再退出。
拿上面的例子来说,如果网易云音乐进程设置为True,那么输出会是这样的:
因为程序执行到输出时间后,主进程已经执行完,网易云音乐进程设置为True,那么主进程不会等网易云进程执行完毕,并随着主进程结束,将此进程一并结束,主进程完成的太快,所以看起来没有执行网易云音乐的子进程。相反,lol进程设置为False,主进程会等他执行完成,所以lol正常执行完毕。
join阻塞进程:
上面的例子中,p1.json()
, 主进程会等p1执行完再执行剩下的代码。此时的输出为:
时间是最后输出的,不同于前面的输出。
如果把上面例子中的p2.join()
注释掉,那么输出的时间会是5秒多,因为主进程在5秒多p1执行完后就继续执行了,此时p2还没有执行完成,主进程没有p2执行完成就继续执行了。
daemon和join区别
总结一下,daemon和join看起来都是等待,区别是daemon是主进程先自己执行完再等待,join是执行到当前代码就等待,到子进程执行完毕再继续主进程。
进程不共享全局变量
from multiprocessing import Process
result = 0
def work01():
global result
result += 1
print(f'work01中result值是{result}')
def work02():
global result
result += 1
print(f'work02中result值是{result}')
def main():
work01()
work02()
print(result)
def process_test():
p1 = Process(target=work01)
p2 = Process(target=work02)
p1.start()
p2.start()
p1.join()
p2.join()
if __name__ == '__main__':
process_test()
print(f'主进程中result的值是:{result}')
此例子中,最终输出的值为0,而不是我们设想的2。
因为每创建一个进程,CPU都会单独分配资源。所以,多个进程不会共享全局变量。
那么如何让多进程共享全局变量呢?此处就说到了队列。
共享进程数据
使用队列共享数据
from multiprocessing import Process, Queue
def work01(q):
r = q.get() # 获取队列的值
r += 1
q.put(r) # 把值放入队列
print(f'work01中q值是{r}')
def work02(q):
r = q.get()
r += 1
q.put(r)
print(f'work02中q值是{r}')
def process_test():
q = Queue() # 创建队列
q.put(0) # 把0放入队列
p1 = Process(target=work01, args=(q,)) # 把队列传给子进程
p2 = Process(target=work02, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
print(f'主进程中q的值是:{q.get()}')
if __name__ == '__main__':
process_test()
此时,我们就实现了输出为2。
使用Pipe共享数据
from multiprocessing import Process, Pipe
def f(conn):
conn.send([42, None, 'hello']) # send方法往管道放入值
conn.close()
if __name__ == '__main__':
parent_conn, child_conn = Pipe() # 定义一个pipe,第一个参数接收父管道,第二个参数接收子管道
p = Process(target=f, args=(child_conn,)) # 将子管道传给子进程
p.start()
print(parent_conn.recv()) # recv方法取出管道中的值
p.join()
自定义进程类
自定义类继承进程类multiprocessing.Process。将进程方法写到run方法中,进程启动时自动调用。
import multiprocessing
import time
class MyProcess(multiprocessing.Process):
def __init__(self):
super(MyProcess, self).__init__()
def run(self): # 进程启动的时候会调用此方法
time.sleep(1)
print(f'Hello!')
if __name__ == '__main__':
p = MyProcess()
p.start()
p.join()