消息列队、进程创建、进程通信IPC机制、守护进程、僵尸进程、孤儿进程、互斥锁
一、创建进程的多种方式
创建进程的本质:在内存中申请一块内存空间运行相应的程序代码
1. 双击桌面程序图标
2. 代码创建进程
强调:不同的操作系统创建进程的要求不一样
在Windows中穿甲进程是以导入模块的方式进行 所以创建进程的代码必须写在__main__子代码中
否则会直接报错 因为在无限制创建进程
在linux和mac中创建进程是直接拷贝一份源代码然后执行 不需要写下__main__子代码中
'''----------------第一种创建进程的方法---------------'''
from 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('主进程')
'''----------------第二种创建进程的方法---------------'''
class MyProcess(Process):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
print(f'{self.name}正在运行')
time.sleep(5)
print(f'{self.name}运行结束')
if __name__ == '__main__':
obj = MyProcess('jason')
obj.start()
print('主进程')
"""
补充说明:
创建进程的代码在不同的操作系统中 底层原理有区别!!!
在windows中 创建进程类似于导入模块
if __name__ == '__main__': 启动脚本
在mac、linux中 创建进程类似于直接拷贝
不需要启动脚本 但是为了兼容性 也可以使用
代码的走向:相当于在一个内存空间有一个主代码,它是从上至下的走,遇到start(),单独再开一个进程,然后紧接着再走它自己的代码
"""
二、join方法
1. join:主进程等待子进程运行结束之后再运行
2. 推导步骤
直接在主进程代码中添加time.sleep(),不合理,因为无法准确把握子进程执行的时间
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('主进程')
'''
p1.start()
p1.join()
p2.start()
p2.join()
p3.start()
p3.join()
end_time = time.time() - start_time
print('总耗时:%s' % end_time) # 六秒多
print('主进程')
'''
'''一定要看准join的执行位置 以及多任务情况下等待的目标'''
三、进程间数隔离
'''
多个进程数据彼此之间默认是互相隔离的
如果真的想交互,需要借助于'管道'或者'队列'
'''
from multiprocessing import Process
money = 100
def task():
global money
money = 666
print('子进程打印的money', money)
if __name__ == '__main__':
p = Process(target=task)
p.start()
p.join()
print('父进程打印的money', money)
四、消息队列
1.相当于一个临时或者是公共的保存数据的地方,进程之间可以通过它来实现交互,可以向它里面放入数据,也可以取出数
2.队列可以再本地上,也可以在网络上,可以单独拿出来变成一个程序,'消息队列'是存在在网络上的,它是由服务器维护的
五、进程间通信IPC机制
进程间通信用到的可以是管道也可以是队列,但是管道没有队列好用
什么是队列?
它的特点是先进先出
from multiprocessing import Queue
# 1.创建队列对象
q = Queue(3) # 括号内指定队列可以容纳的数据个数 默认是:2147483647
# 2.往队列添加数据
q.put('张')
q.put('询')
q.put('彬')
q.put('吃屁') # 超出数据存放极限 那么程序一致处于阻塞态 直到队列中有数据被取出
print(q.full()) # 判断队列是否已经存满
# 3.从队列中取数据
print(q.get_nowait())
print(q.get_nowait())
print(q.get_nowait()) # 队列中如果没有数据可取 直接报错
print(q.get())
print(q.empty()) # 判断队列是否已经空了,返回的是布尔值
print(q.get())
print(q.get())
print(q.empty())
print(q.get()) # 超出数据获取极限 那么程序一致处于阻塞态 直到队列中有数据被添加
"""
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('主进程')
六、生产者消费者模型
生产者
产生数据
消费者
处理数据
"""
回忆过去 爬取红牛分公司
生产者:获取网页数据的代码(函数)
爬
消费者:从网页数据中筛选出符合条件的数据(函数)
筛选
完整的生产者消费者模型至少有三个部分
生产者
消息队列/数据库
消费者
"""
七、进程对象相关方法
1. 查看进程号
from multiprocessing import current_process
import os
current_process().pid
os.getpid()
os.getppid()
2. 销毁子进程
p1.terminate()
3. 判断进程是否存活
p1.is_alive()
八、守护进程、僵尸进程与孤儿进程
1. 守护进程
如何理解守护
伴随着守护对象的存活而存活 死亡而死亡
from multiprocessing import Process
import time
def task(name):
print('大内总管:%s存活' % name)
time.sleep(3)
print('大内总管:%s嗝屁' % name)
if __name__ == '__main__':
p = Process(target=task, args=('基佬',))
# p.daemon = True # 将子进程设置为守护进程:主进程代码结束 子进程立刻结束
p.start()
p.daemon = True # 必须在start之前执行
print('天子Jason寿终正寝!!!')
1. 僵尸进程
进程已经运行结束 但是相关的资源并没有完全清空
需要父进程参与回收
2. 孤儿进程
父进程意外死亡 子进程正常运行 该子进程就称之为孤儿进程
孤儿进程也不是没有人管 操作系统会自动分配福利院接收
九、互斥锁
模拟抢票
查票
买票
查票
买票
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()