进程进阶
进程进阶
1、子进程回收资源的两种方式
1、join让主进程等待子进程结束后再结束,并回收子进程资源,主进程结束并回收资源
2、未使用join,并且主进程 “ 正常结束 ” ,子进程与主进程一并被回收资源
2、僵尸进程与孤儿进程(了解)
1、僵尸进程
在子进程结束后,主进程没有正常结束,子进程PID不会被回收
缺点:操作系统中的PID号是有限的,如有子进程PID 号,无法正常回收,则会占用PID号,浪费资源,若PID号满了,则无法创建新的进程
2、孤儿进程
在子进程没有结束时,主进程没有 “ 正常结束” ,子进程PID不会被回收
操作系统有优化进制(孤儿院):
当主进程意外终止,操作系统会检测是否有正在运行的子进程,会把他们放到孤儿院中,让操作系统帮忙回收
3、守护进程
当主进程结束时,子进程也必须结束,并回收
在不使用join的情况下,使用obj.daemon = True,设置在obj.start()之前,让主进程结束,子进程也跟着结束,并回收
from multiprocessing import Process
import time
def data():
print('子进程start')
time.sleep(10)
print('子进程end')
if __name__ == '__main__':
obj = Process(target=data)
# 守护进程必须在obj.start()之前设置
obj.daemon = True
obj.start()
time.sleep(1)
print('主进程结束')
》》》 # 子进程start
# 主进程结束
4、进程之间的数据时隔离的
由于创建进程是开辟新的内存空间,所以进程之间的数据是相互隔离的
# 进程之间的数据是隔离的
from multiprocessing import Process
num = 100
def data1(num):
num += 1
# 此处打印的是子进程data1的num,子进程与主进程数据是隔离的
print(num) # 11
def data2():
global num
num += 10
# 此处打印的是子进程data2的num,子进程与主进程数据是隔离的
print(num) # 110
if __name__ == '__main__':
obj1 = Process(target=data1, args=(10,))
obj2 = Process(target=data2)
obj1.start()
obj2.start()
obj2.join()
obj1.join()
# 此处打印的是主进程的num,子进程与主进程数据是隔离的
print(num) # 100
print(num) # 100
5、进程互斥锁
互斥锁是用来保证数据读写安全的,在修改同一个数据时,同一时间只能有一个任务可以进行修改,即串行的修改,保证数据安全
# json_data文件中{"num": 0} ,json数据里面必须是双引号
from multiprocessing import Process
from multiprocessing import Lock
import json
import time
import random
lock = Lock()
# 查票
def search(name):
with open('json_data.json', 'r', encoding= 'utf-8') as f:
data_dic = json.load(f)
print(f'用户{name}查看余票,余票{data_dic.get("num")}')
# 买票
def buy(name):
# 网络延时
with open('json_data.json', 'r', encoding= 'utf-8') as f:
data_dic = json.load(f)
if data_dic.get('num') > 0:
data_dic['num'] -= 1
time.sleep(random.random())
with open('json_data.json', 'w', encoding= 'utf-8') as f:
json.dump(data_dic,f)
print(f'用户{name}抢票成功')
else:
print(f'用户{name}抢票失败')
def run(name,lock):
search(name)
# 加锁
lock.acquire()
buy(name)
# 释放锁
lock.release()
if __name__ == '__main__':
# 开启多进程,实现并发
for i in range(10):
obj = Process(target=run, args=(f'jason{i},',lock))
obj.start()
6、队列(FIFO)
队列(FIFO):先进先出:先存放的数据,就先取出来
堆栈(FILO):先进后出
相当于第三方的管道,可以存放数据
应用 :让进程之间的数据可以交互
有三种调用方式:
使用Queue(5)指的是队列中只能存放5份数据,默认长度是32,生成一个对象通过obj.put( x )往队列中放数据,或者使用obj.put_nowait( x )往队列中存放数据,使用obj.get()或obj.get_nowait()来获取队列中的数据
put :只要队列满了,就会进入阻塞
put_nowait :队列满了,就会报错
get:只要队列中有数据,就能获取数据,若没有则会进入阻塞
get_nowait : 只要队列中有数据,就能获取数据,若没有则会报错
from multiprocessing import Queue # multiprocessing提供队列 先进先出
from multiprocessing import JoinableQueue # 基于 Queue 封装的队列 先进先出
import queue # python内置的队列 先进先出
Queue(5)指的是队列中只能存放5份数据,默认长度是32
# 第一种:
# 生成队列对象
obj = Queue(5)
# 第二种:
# 生成队列对象
obj = JoinableQueue(5)
# 第三种:
# 生成队列对象
obj = queen.Queue(5)
# 添加数据到队列中
obj.put('1')
print('添加一个数1')
obj.put('2')
print('添加一个数2')
obj.put('3')
print('添加一个数3')
obj.put('4')
print('添加一个数4')
obj.put('5')
print('添加一个数5')
# put :只要队列满了,就会进入阻塞
# obj.put('6')
# print('添加一个数6')
# put_nowait :队列满了,就会报错
# obj.put_nowait('6')
# print('添加一个数6')
print(obj.get())
print(obj.get())
print(obj.get())
print(obj.get())
print(obj.get())
# get:只要队列中有数据,就能获取数据,若没有则会进入阻塞
# print(obj.get())
# get_nowait : 只要队列中有数据,就能获取数据,若没有则会报错
# print(obj.get_nowait())
7、IPC机制
IPC机制:是进程之间实现通信,使用队列让两个进程之间的数据进行交互,实现进制之间的通信
from multiprocessing import Process
from multiprocessing import JoinableQueue
import time
def task1(q):
x = 100
q.put(x)
print(f'添加数据{x}到队列中')
time.sleep(2)
# 获取队列中的数据
# task2中又放了数据到队列中被获取到
print(q.get()) # 200
def task2(q):
# 获取队列中的数据,task1中先放了数据先获取到task1中的数据
res = q.get()
print(f'获取的数据{res}') # 获取的数据100
# 放数据到队列中
q.put(200)
if __name__ == '__main__':
q = JoinableQueue(5)
# 先放task1数据
p1 = Process(target=task1, args=(q,))
# 再放task2数据
p2 = Process(target=task2, args=(q,))
p1.start()
p2.start()
8、生产者与消费者
生产者:生产数据的
消费者:使用数据的
有两种情况,1、生产者生产的大于消费者所需的,供大于求;2、生产者生产的不够消费者使用的,供不应求
生产者:往队列中加数据
消费者:从队列中取数据
from multiprocessing import JoinableQueue
from multiprocessing import Process
import time
# 生产者:生产数据——》队列
def producer(name, food, q):
msg = f'{name}生产了{food}'
# 往队列中加数据
q.put(food)
print(msg)
# 消费者: 从队列中使用数据
def customer(name, q):
while True:
try:
time.sleep(1)
# 从队列中取数据
food = q.get()
msg = f'{name}消费了{food}'
print(msg)
except Exception:
break
if __name__ == '__main__':
# 默认长度32
q = JoinableQueue()
# 创建生产者
for i in range(10):
p1 = Process(target=producer, args=('tank', f'油条{i}', q))
p1.start()
# 创建两个消费者
c1 = Process(target=customer, args=('Mr沈', q))
c2 = Process(target=customer, args=('shen', q))
c1.start()
c2.start()