进程进阶知识

108 阅读5分钟

创建进程

我们平常双击打开一个软件就相当于告诉操作系统,创造了一个进程。我们当然也可以在python代码中来创建进程
方式1from multiprocessing import Process
import time
def task(name):
    print(f'进程{name}正在运行')
    time.sleep(3)
    print(f'进程{name}结束运行')
if __name__ == '__main__':
    p = Process(target=task, args=('jason',))  # 创建一个进程对象
    p.start()  # 告诉操作系统创建一个进程(异步操作)
    # task('jason')  # 普通的函数调用是同步操作
    print('主进程结束')
    
    
打印结果如下:    
# 主进程结束
# 进程jason正在运行
# 进程jason结束运行


方式2class MyProcess(Process):
    def __init__(self, name):
        super().__init__()
        self.name = name
    def run(self):
        print(f'{self.name}正在运行')
        time.sleep(3)
        print(f'{self.name}结束运行')

if __name__ == '__main__':
    obj = MyProcess('jason')
    obj.start()
    print('主进程')

'''
创建进程的代码在不同的操作系统中 底层原理是有区别的
在windows中 创建进程类似于导入模块
	if __name__ == '__name__': 启动脚本
在mac、linux中 创建进程类似于直接拷贝
	不需要启动脚本 但是为了兼容性 也可以使用
'''    

1.jpg

join方法

作用:让主进程等待子进程结束之后再运行

def task(name, n):
    print(f'{name}正在运行')
    time.sleep(n)
    print(f'{name}运行结束')

if __name__ == '__main__':
    p1 = Process(target=task, args=('jason', 1))  # args就是通过元组的形式给函数传参
    p2 = Process(target=task, args=('kevin', 2))  # 也可以通过kwargs={'name':'jason', 'n':1} 太麻烦 没必要
    p3 = Process(target=task, args=('jerry', 3))
    start_time = time.time()
    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    end_time = time.time() - start_time
    print('总耗时:%s' % end_time)
    print('主进程')
'''一定要看准join的执行位置 以及多任务情况下等待的目标'''

2.jpg

进程间的通信

from multiprocessing import Process

money = 100
def task():
    money = 666
    print(f'子进程的money:{money}')  # 666
if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()
    print(f'父进程打印的money:{money}')  # 100

多个进程间的数据默认隔离,如果真的想交互,需要借助于'管道'或者'队列'

预备知识:
    什么是队列:先进先出
       from multiprocessing import Queue
    # 1.创建队列对象
    q = Queue(3)  # 括号内指定队列可以容纳的数据个数 默认:2147483647
    # 2.往队列添加数据
    # q.put(111)
    # print(q.full())  # 判断队列是否已经存满
    # q.put(222)
    # q.put(333)
    # print(q.full())
    # q.put(444)  # 超出数据存放极限 那么程序一致处于阻塞态 直到队列中有数据被取出
    # 3.从队列中取数据
    # print(q.get())
    # print(q.empty())  # 判断队列是否已经空了
    # print(q.get())
    # print(q.get())
    # print(q.empty())
    # print(q.get())  # 超出数据获取极限 那么程序一致处于阻塞态 直到队列中有数据被添加
    # print(q.get_nowait())
    # print(q.get_nowait())
    # print(q.get_nowait())  # 队列中如果没有数据可取 直接报错
"""
    q.full()  # 判满
    q.empty()  # 判空
    q.get_nowait()  # 取值
    上述方法在多进程下不能准确使用(失效)!!!
"""    

IPC机制
1.主进程与子进程通信
2.子进程与子进程通信
 
from multiprocessing import Queue, Process

def procedure(q):
    q.put('子进程procedure往队里中添加了数据')

def consumer(q):
    print('子进程的consumer从队列中获取数据', q.get())


if __name__ == '__main__':
    q = Queue()  # 在主进程中产生q对象 确保所有的子进程使用的是相同的q
    p1 = Process(target=procedure, args=(q,))
    p2 = Process(target=consumer, args=(q,))
    p1.start()
    p2.start()
    print('主进程')

生产者消费者模型

生产者:产生数据(类似于我们从网站爬取数据)
消息队列/数据库
消费者:处理数据(类似于我们从爬取的数据集中筛选自己想要的数据)

进程相关方法

from multiprocessing import current_process
import os
1.查看进程号:
    current_process().pid
    os.getpid()  # 效果等同于上述方法,但更为灵活
    os.getppid()  # 查看主(父)进程的进程号
2.销毁子进程
    p.terminate()
3.判断进程是否存活
    p.is_alive()

守护进程

如何理解守护进程:伴随守护对象的存活而存活,死亡而死亡
from multiprocessing import Process
import time

def task(name):
    print(f'守护进程{name}存活')
    time.sleep(2)
    print(f'守护进程{name}嗝屁')

if __name__ == '__main__':
    p = Process(target=task, args=('大仓',))
    p.daemon = True  # 将子线程设置为守护进程,主进程结束守护进程立刻结束
    p.start()
    # p.daemon = True  # 注意只能在进程出生之前设置,不能设置在start后面
    print('主进程驾崩')
    
打印结果    
#     主进程驾崩
没有使用join方法,所以主进程正常优先于子进程执行。由于将子进程设置为了守护进程,所以主进程一结束,子进程也立刻嗝屁,不会执行task方法

僵尸进程与孤儿进程

僵尸进程:
    进程已经运行结束,但是相关的资源并没有立即完全清空,需要父进程参与回收。所有进程都会经历僵尸进程
孤儿进程:
    父进程意外死亡,子进程正常运行,该子进程就称为孤儿进程
    孤儿进程也不是没有人管,操作系统会自动分配'福利院'接收	

模拟抢票

from multiprocessing import Process
import time
import json
import random


# 查票
def search(name):
    with open(r'data.json', 'r', encoding='utf8') as f:
        data = json.load(f)
    print('%s在查票 当前余票为:%s' % (name, data.get('ticket_num')))


# 买票
def buy(name):
    # 再次确认票
    with open(r'data.json', 'r', encoding='utf8') as f:
        data = json.load(f)
    # 模拟网络延迟
    time.sleep(random.randint(1, 3))
    # 判断是否有票 有就买
    if data.get('ticket_num') > 0:
        data['ticket_num'] -= 1
        with open(r'data.json', 'w', encoding='utf8') as f:
            json.dump(data, f)
        print('%s买票成功' % name)
    else:
        print('%s很倒霉 没有抢到票' % name)


def run(name):
    search(name)
    buy(name)


if __name__ == '__main__':
    for i in range(10):
        p = Process(target=run, args=('用户%s'%i, ))
        p.start()