目录
-
Python并发编程03/僵尸孤儿进程,互斥锁,进程之间的通信
- 1.昨日回顾
-
2.僵尸进程和孤儿进程
- 2.1僵尸进程
- 2.2孤儿进程
- 2.3僵尸进程如何解决?
-
3.互斥锁,锁
- 3.1互斥锁的应用
- 3.2Lock与join的区别
-
4.进程之间的通信
- 进程在内存级别是隔离的
- 4.1基于文件通信 (抢票系统)
- 4.2基于队列通信
Python并发编程03/僵尸孤儿进程,互斥锁,进程之间的通信
1.昨日回顾
1.创建进程的两种方式: 函数, 类.
2.pid: os.getpid() os.getppid()
tasklist
tasklist| findstr 进程名
3.进程与进程之间是有物理隔离: 不能共享内存的数据.(lock,队列)
4.join阻塞: 让主进程等待子进程结束之后,在执行.
5.其他属性: terminate() is_alive() name,
6.守护进程: 将子进程设置成守护进程,当主进程结束了,子进程就马上结束.
2.僵尸进程和孤儿进程
2.1僵尸进程
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程.
#基于unix环境(linux,macOS)
#主进程需要等待子进程结束之后,主进程才结束
主进程时刻监测子进程的运行状态,当子进程结束之后,一段时间之内,将子进程进行回收.
#为什么主进程不在子进程结束后马上对其回收呢?
# 1. 主进程与子进程是异步关系.主进程无法马上捕获子进程什么时候结束.
# 2. 如果子进程结束之后马上再内存中释放资源,主进程就没有办法监测子进程的状态了.
#unix针对于上面的问题,提供了一个机制.
#所有的子进程结束之后,立马会释放掉文件的操作链接,内存的大部分数据,但是会保留一些内容: 进程号,结束时间,运行状态,等待主进程监测,回收.
僵尸进程: 所有的子进程结束之后,在被主进程回收之前,都会进入僵尸进程状态.
#僵尸进程有无危害???
#如果父进程不对僵尸进程进行回收(wait/waitpid),产生大量的僵尸进程,这样就会占用内存,占用进程pid号.
2.2孤儿进程
#父进程由于某种原因结束了,但是你的子进程还在运行中,这样你的这些子进程就成了孤儿进程.你的父进程如果结束了,你的所有的孤儿进程就会被init进程的回收,init就变成了你的父进程,对你进行回收.
2.3僵尸进程如何解决?
#父进程产生了大量子进程,但是不回收,这样就会形成大量的僵尸进程,解决方式就是直接杀死父进程,将所有的僵尸进程变成孤儿进程进程,由init进行回收.
3.互斥锁,锁
3.1互斥锁的应用
# 三个同事 同时用一个打印机打印内容.
# 三个进程模拟三个同事, 输出平台模拟打印机.
# 版本一:
# from multiprocessing import Process
# import time
# import random
# import os
#
# def task1():
# print(f'{os.getpid()}开始打印了')
# time.sleep(random.randint(1,3))
# print(f'{os.getpid()}打印结束了')
#
# def task2():
# print(f'{os.getpid()}开始打印了')
# time.sleep(random.randint(1,3))
# print(f'{os.getpid()}打印结束了')
#
# def task3():
# print(f'{os.getpid()}开始打印了')
# time.sleep(random.randint(1,3))
# print(f'{os.getpid()}打印结束了')
#
# if __name__ == '__main__':
#
# p1 = Process(target=task1)
# p2 = Process(target=task2)
# p3 = Process(target=task3)
#
# p1.start()
# p2.start()
# p3.start()
# 现在是所有的进程都并发的抢占打印机,
# 并发是以效率优先的,但是目前我们的需求: 顺序优先.
# 多个进程共强一个资源时, 要保证顺序优先: 串行,一个一个来.
# 版本二:
# from multiprocessing import Process
# import time
# import random
# import os
#
# def task1(p):
# print(f'{p}开始打印了')
# time.sleep(random.randint(1,3))
# print(f'{p}打印结束了')
#
# def task2(p):
# print(f'{p}开始打印了')
# time.sleep(random.randint(1,3))
# print(f'{p}打印结束了')
#
# def task3(p):
# print(f'{p}开始打印了')
# time.sleep(random.randint(1,3))
# print(f'{p}打印结束了')
#
# if __name__ == '__main__':
#
# p1 = Process(target=task1,args=('p1',))
# p2 = Process(target=task2,args=('p2',))
# p3 = Process(target=task3,args=('p3',))
#
# p2.start()
# p2.join()
# p1.start()
# p1.join()
# p3.start()
# p3.join()
# 我们利用join 解决串行的问题,保证了顺序优先,但是这个谁先谁后是固定的.
# 这样不合理. 你在争抢同一个资源的时候,应该是先到先得,保证公平.
# 版本3:
from multiprocessing import Process
from multiprocessing import Lock
import time
import random
import os
def task1(p,lock):
'''
一把锁不能连续锁两次
lock.acquire()
lock.acquire()
lock.release()
lock.release()
'''
lock.acquire()
print(f'{p}开始打印了')
time.sleep(random.randint(1,3))
print(f'{p}打印结束了')
lock.release()
def task2(p,lock):
lock.acquire()
print(f'{p}开始打印了')
time.sleep(random.randint(1,3))
print(f'{p}打印结束了')
lock.release()
def task3(p,lock):
lock.acquire()
print(f'{p}开始打印了')
time.sleep(random.randint(1,3))
print(f'{p}打印结束了')
lock.release()
if __name__ == '__main__':
mutex = Lock()
p1 = Process(target=task1,args=('p1',mutex))
p2 = Process(target=task2,args=('p2',mutex))
p3 = Process(target=task3,args=('p3',mutex))
p2.start()
p1.start()
p3.start()
3.2Lock与join的区别
#共同点: 都可以把并发变成串行, 保证了顺序.
#不同点: join人为设定顺序,lock让其争抢顺序,保证了公平性.
4.进程之间的通信
进程在内存级别是隔离的
4.1基于文件通信 (抢票系统)
# 抢票系统.
# 1. 先可以查票.查询余票数. 并发
# 2. 进行购买,向服务端发送请求,服务端接收请求,在后端将票数-1,返回到前端. 串行.
# from multiprocessing import Process
# import json
# import time
# import os
# import random
#
#
# def search():
# time.sleep(random.randint(1,3)) # 模拟网络延迟(查询环节)
# with open('ticket.json',encoding='utf-8') as f1:
# dic = json.load(f1)
# print(f'{os.getpid()} 查看了票数,剩余{dic["count"]}')
#
#
# def paid():
# with open('ticket.json', encoding='utf-8') as f1:
# dic = json.load(f1)
# if dic['count'] > 0:
# dic['count'] -= 1
# time.sleep(random.randint(1,3)) # 模拟网络延迟(购买环节)
# with open('ticket.json', encoding='utf-8',mode='w') as f1:
# json.dump(dic,f1)
# print(f'{os.getpid()} 购买成功')
#
# def task():
# search()
# paid()
#
#
# if __name__ == '__main__':
#
# for i in range(6):
# p = Process(target=task)
# p.start()
# 当多个进程共强一个数据时,如果要保证数据的安全,必须要串行.
# 要想让购买环节进行串行,我们必须要加锁处理.
#
# from multiprocessing import Process
# from multiprocessing import Lock
# import json
# import time
# import os
# import random
#
#
# def search():
# time.sleep(random.randint(1,3)) # 模拟网络延迟(查询环节)
# with open('ticket.json',encoding='utf-8') as f1:
# dic = json.load(f1)
# print(f'{os.getpid()} 查看了票数,剩余{dic["count"]}')
#
#
# def paid():
# with open('ticket.json', encoding='utf-8') as f1:
#
# dic = json.load(f1)
# if dic['count'] > 0:
# dic['count'] -= 1
# time.sleep(random.randint(1,3)) # 模拟网络延迟(购买环节)
# with open('ticket.json', encoding='utf-8',mode='w') as f1:
# json.dump(dic,f1)
# print(f'{os.getpid()} 购买成功')
#
#
# def task(lock):
# search()
# lock.acquire()
# paid()
# lock.release()
#
# if __name__ == '__main__':
# mutex = Lock()
# for i in range(6):
# p = Process(target=task,args=(mutex,))
# p.start()
# 当很多进程抢一个资源(数据)时, 你要保证顺序(数据的安全),一定要串行.
# 互斥锁: 可以公平性的保证顺序以及数据的安全.
# 基于文件的进程之间的通信:
# 效率低.
# 自己加锁麻烦而且很容易出现死锁.
4.2基于队列通信
队列: 把队列理解成一个容器,这个容器可以承载一些数据,
队列的特性: 先进先出永远保持这个数据. FIFO(first in first out).
# from multiprocessing import Queue
# q = Queue()
# def func():
# print('in func')
# q.put(1)
# q.put('alex')
# q.put([1,2,3])
# q.put(func)
#
#
# print(q.get())
# print(q.get())
# print(q.get())
# f = q.get()
# f()
# from multiprocessing import Queue
# q = Queue(3)
#
# q.put(1)
# q.put('alex')
# q.put([1,2,3])
# # q.put(5555) # 当队列满了时,在进程put数据就会阻塞.
# # q.get()
#
# print(q.get())
# print(q.get())
# print(q.get())
# print(q.get()) # 当数据取完时,在进程get数据也会出现阻塞,直到某一个进程put数据.
# from multiprocessing import Queue
# q = Queue(3) # maxsize
#
# q.put(1)
# q.put('alex')
# q.put([1,2,3])
# q.put(5555,block=False)
#
# print(q.get())
# print(q.get())
# print(q.get())
# print(q.get(timeout=3)) # 阻塞3秒,3秒之后还阻塞直接报错.
# print(q.get(block=False))
# block=False 只要遇到阻塞就会报错.