1.守护进程
指的也是一个进程,可以守护着另一个进程
一个进程a设置为b的守护进程 当b结束时 a会立马结束自己不管任务是否执行完毕
使用场景:例如qq进程 有一个下载任务 交给了一个子进程
但是过程中 qq退出了 下载进程也会随之关闭了
import time from multiprocessing import Process def task(): print('妃子的一生。。。') time.sleep(10) print('妃子挂了。。。') if __name__ == '__main__': print('皇帝登基!') p = Process(target=task) # 将p子进程设置为主进程的守护进程 必须放在开启进程之前设置 p.daemon = True print('有一个妃子', p) p.start() time.sleep(5) print('皇帝驾崩了。。。')
2.进程安全
进程安全问题:
当多个进程要同时操作同一个资源时,就可能出现问题
例如:
只有一台打印机 但是很多打印任务 如果不加以控制 可能造成打印结果错乱
解决方案1:加join 将原本的并发的任务变成串行执行
但是如此一来 进程与进程之间不再平等 顺序已经固定死了
最终的解决方案就是加锁
使用Lock来实例化产生一把锁
但是要保证每个进程访问的都是同一把锁
在访问共享资源前 加锁
访问后一定要解锁
注意:不能多次执行acquire 一次acquire 对应一次release
acquire时一个阻塞函数 会一直等到锁被释放(release调用)才会继续执行
加锁之后并发又变成了串行了 不过与join不同的是 没有规定顺序 谁先抢到谁先执行
问题:一旦加锁 效率降低 不加锁数据要错乱
from multiprocessing import Process, Lock import time import random def task1(lock): lock.acquire() print('hello my name is aaa') time.sleep(random.randint(1, 3)) print('sjh age is 18') time.sleep(random.randint(1, 3)) print('sjh sex is man') lock.release() def task2(lock): lock.acquire() print('hello my name is yy') time.sleep(random.randint(1, 3)) print('yy age is 18') time.sleep(random.randint(1, 3)) print('yy sex is woman') lock.release() def task3(lock): lock.acquire() print('hello my name is my') time.sleep(random.randint(1, 3)) print('my age is 18') time.sleep(random.randint(1, 3)) print('my sex is woman') lock.release() if __name__ == '__main__': lock = Lock() p1 = Process(target=task1, args=(lock,)) p2 = Process(target=task2, args=(lock,)) p3 = Process(target=task3, args=(lock,)) p1.start() # p1.join() p2.start() # p2.join() p3.start() # p3.join() print('over')
互斥锁
互相排斥对方的锁
a在执行b就滚一边去
在使用锁的时候 无可避免的会降低效率
需要找到一个最合适的地方加上锁
你锁住的代码越少效率越高
join和锁
join是让整个进程中的代码全部串行 而锁可以让部分代码串行
粒度(被锁住的代码量)越小 效率越高
抢票案例
服务器器要存储票数
客户端要查看票数
如果票数大于0就可以购买
#其中的'data.json'为 {"ticket": 2} import json, time, random, os from multiprocessing import Process, Lock def task(lock, name): # 查看余票 check_ticket(name) # 买票会修改数据 必须加锁 lock.acquire() buy_ticket(name) lock.release() def check_ticket(name): with open('data.json', 'rt', encoding='utf-8') as f: ticket = json.load(f)['ticket'] print('%s查看了票数 剩余票数:%s' % (name, ticket)) def buy_ticket(name): # 先查询是否有票 # time.sleep(random.randint(1, 4)) with open('data.json', 'rt', encoding='utf-8')as f: ticket_dic = json.load(f) if ticket_dic['ticket'] > 0: # 买票 需要发送请求 模拟延迟 time.sleep(random.randint(1, 4)) ticket_dic['ticket'] -= 1 with open('data.json', 'wt', encoding='utf-8') as f1: json.dump(ticket_dic, f1) print('%s购买成功!' % name) else: print('没有票了!') if __name__ == '__main__': lock = Lock() p1 = Process(target=task, args=(lock, 'apple')) p2 = Process(target=task, args=(lock, 'lemon')) p3 = Process(target=task, args=(lock, 'mango')) p1.start() # p1.join() p2.start() # p2.join() p3.start()
3.IPC进程间通讯
进程与进程之间内存是物理隔离的 无法直接通讯
例如:qq要调用浏览器 来显示某个网页 网页地址就必须想办法告诉浏览器
四种方式:
1.使用一个共享文件 在硬盘创建一个文件 不同进程之间共享这个文件
优点:交换的数据量几乎没有限制
缺点:速度慢
2.系统开辟一块共享内存 以供进程间交换数据
优点:速度快
缺点:数据量不能太大
3.管道
优点:封装了文件的打开 关闭等操作
缺点:速度慢 并且是单向的 编程复杂度高
4.socket
不仅可以用于与远程计算机中的进程通讯 还可以用于与本地进程通讯
基于内存的 速度快
from multiprocessing import Process, Manager, Lock import time def task(dic, lock): lock.acquire() i = dic['num'] time.sleep(0.3) dic['num'] = i - 1 lock.release() pass if __name__ == '__main__': # 共享内存管理器 m = Manager() # 在共享内存区域 创建了一个字典 # dic = m.dict({'name': 'sjh'}) dic = m.dict({'num': 10}) lock = Lock() a = 100 p1 = Process(target=task, args=(dic, lock)) p2 = Process(target=task, args=(dic, lock)) p3 = Process(target=task, args=(dic, lock)) p1.start() p2.start() p1.join() p2.join() print(dic['num'])
manager
queue
队列也是一个容器
特点:先进先出
支持进程间共享
自动处理了进程安全问题(加锁)
例如:迅雷下载 先添加的任务一定先开始执行
堆栈:
特点:先进后出
例如:函数的执行
from multiprocessing import Queue q = Queue(2) q.put('1') q.put('2') # print(q.get()) # q.put('3') # 会阻塞直到有空位置为止 # print(q.get()) # print(q.get()) # print(q.get()) # 会阻塞直到有数据为止 # # block阻塞 # timeout等待超时 只在block为True时有效 q.put('3', block=True, timeout=5) queue的使用 from multiprocessing import Process, Queue import time def task(q): num = q.get() # print(num) time.sleep(0.5) q.put(num - 1) if __name__ == '__main__': q = Queue() q.put(3) # 将处于共享内存区域的字典传给子进程 p1 = Process(target=task, args=(q,)) p2 = Process(target=task, args=(q,)) p3 = Process(target=task, args=(q,)) p1.start() p2.start() p3.start() p1.join() p2.join() p3.join() print(q.get())
4.生产者消费者模型
需求:一共要生产 并消费10台手机
由于 生产者和消费者能力不匹配(处理速度不一致)导致双方可能互相等待 造成了效率的降低
1.将原本由一个进程完成的 两个(生成和消费)拆分为两个不交给两个不同的角色(进程)来完成
2.由于进程间内存互相隔离 所以需要为两个角色之间提供一个共享的数据容器
3.生产将生产完成的数据放入容器中
4.消费者从容器中取出数据来处理
生产者消费者模型的优点:
1.平衡了生产者和消费者的能力差异 提高处理效率
2.降低了双方耦合度
学习并发的两个核心问题 安全性和效率
from multiprocessing import Process, Queue import time, random # 生产者 def xiaomi_factory(name, q): for i in range(1, 6): time.sleep(random.randint(1, 3)) phone = '这是%s生产的小米9-%s' % (name, i) print('生产者 生产了%s' % phone) # 放到柜台即存储到队列中 q.put(phone) # 消费者 def buy_mi9(q): for i in range(10): # 从队列中取需要被处理的数据(phone) time.sleep(random.randint(1, 2)) phone = q.get() print('消费者 消费了%s' % phone) if __name__ == '__main__': # 共享数据的队列 q = Queue() # 生产者 p1 = Process(target=xiaomi_factory, args=('北京公司', q)) p2 = Process(target=xiaomi_factory, args=('上海公司', q)) p1.start() p2.start() # 消费者 c1 = Process(target=buy_mi9, args=(q,)) c1.start()