进程进阶

进程进阶

一、子进程回收资源的两种方式

​ 一、join让主进程等待子进程结束后再结束,并回收子进程资源,主进程结束并回收资源python

​ 二、未使用join,而且主进程 “ 正常结束 ” ,子进程与主进程一并被回收资源json

二、僵尸进程与孤儿进程(了解)

一、僵尸进程安全

​ 在子进程结束后,主进程没有正常结束,子进程PID不会被回收网络

​ 缺点:操做系统中的PID号是有限的,若有子进程PID 号,没法正常回收,则会占用PID号,浪费资源,若PID号满了,则没法建立新的进程并发

二、孤儿进程dom

在子进程没有结束时,主进程没有 “ 正常结束” ,子进程PID不会被回收优化

操做系统有优化进制(孤儿院):ui

​ 当主进程意外终止,操做系统会检测是否有正在运行的子进程,会把他们放到孤儿院中,让操做系统帮忙回收操作系统

三、守护进程

当主进程结束时,子进程也必须结束,并回收code

在不使用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
		# 主进程结束

四、进程之间的数据时隔离的

因为建立进程是开辟新的内存空间,因此进程之间的数据是相互隔离的

# 进程之间的数据是隔离的
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

五、进程互斥锁

互斥锁是用来保证数据读写安全的,在修改同一个数据时,同一时间只能有一个任务能够进行修改,即串行的修改,保证数据安全

# 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()

六、队列(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())

七、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()

八、生产者与消费者

生产者:生产数据的

消费者:使用数据的

有两种状况,一、生产者生产的大于消费者所需的,供大于求;二、生产者生产的不够消费者使用的,供不该求

生产者:往队列中加数据

消费者:从队列中取数据

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()