并发进阶
- 僵尸进程与孤儿进程
- 进程对象及其他方法
-
- windows查找进程:tasklist |findstr 进程id号
- mac,Linux查找进程:ps aux | grep 进程id号
- 获取进程对象:t=Process(target=task, )或者是在进程内部:current_process()
- t.pid或者current_process().pid:获取进程id号
- os.getpid() 同上,获取进程id号
- os.getppid() 获取父进程id号,子进程中获取父进程id,等于父进程的id号,主进程的父进程是python解释器
- t.is_alive()或者current_process().is_alive() :查看进程是否存活
- t.terminate() 关闭进程,在主进程关闭, 关闭时会有短暂延迟
- 案例
- 守护进程:主进程一旦结束,子进程也结束(子进程的属性)
- 互斥锁:让并发的某一步变为串行
- 队列 Queue
- 进程间通信-ipc
僵尸进程与孤儿进程
僵尸进程:进程结束了,资源还没来得及回收
孤儿进程:主进程挂了,子进程还没结束,它就会被专门的进程接管
进程对象及其他方法
windows查找进程:tasklist |findstr 进程id号
mac,Linux查找进程:ps aux | grep 进程id号
获取进程对象:t=Process(target=task, )或者是在进程内部:current_process()
t.pid或者current_process().pid:获取进程id号
os.getpid() 同上,获取进程id号
os.getppid() 获取父进程id号,子进程中获取父进程id,等于父进程的id号,主进程的父进程是python解释器
t.is_alive()或者current_process().is_alive() :查看进程是否存活
t.terminate() 关闭进程,在主进程关闭, 关闭时会有短暂延迟
案例
from multiprocessing import Process,current_process
import time
import os
# 每个进程都会有自己的id号pid
def task():
print('子进程')
# 当前进程的id号
print(current_process().pid)
print(os.getpid()) # 跟上面打印出来是一模一样的
# 取出该进程父id号
print('-----',os.getppid())
# current_process()当前进程对象
print(current_process().is_alive())
time.sleep(2)
print('子进程结束')
if __name__ == '__main__':
t = Process(target=task, )
t.start()
print(t.pid)
time.sleep(0.5)
t.terminate() # 把t进程关闭
time.sleep(0.1) # 如果这里没有停顿0.1秒,下一行的t.isalive()返回的是True,因为子进程还没关闭完成
print('主进程打印的结果', t.is_alive()) # False
守护进程:主进程一旦结束,子进程也结束(子进程的属性)
from multiprocessing import Process, current_process
import time
import os
def task():
# print(os.getpid())
print('子进程')
time.sleep(200)
print('子进程结束')
if __name__ == '__main__':
t = Process(daemon=True ,target=task, )
# 守护进程:主进程一旦结束,子进程也结束,附加给子进程的属性
# t.daemon=True # 如果没有传入daemon=True参数,这一行一定要加在启动之前
t.start()
time.sleep(1)
print('主进程结束')
print(os.getppid())
# time.sleep(100)
互斥锁:让并发的某一步变为串行
多人抢一票案例
# 同时只有一个人能拿到,必须释放,其他人才能再次获取到
from multiprocessing import Process, Lock
import json
import time
import random
# ticket文件中只存入了一张票
def search():
# 查票的函数
# 打开文件,读出ticket_count
with open('ticket', 'r', encoding='utf-8') as f:
dic = json.load(f)
print('余票还有:', dic.get('ticket_count'))
def buy():
with open('ticket', 'r', encoding='utf-8') as f:
dic = json.load(f)
time.sleep(random.randint(1, 3)) # 模拟一下网络延迟
if dic.get('ticket_count') > 0:
# 能够买票
dic['ticket_count'] -= 1
# 保存到文件中去
with open('ticket', 'w', encoding='utf-8') as f:
json.dump(dic, f)
print('买票成功')
else:
# 买票失败
print('买票失败')
# 写一个函数,先查票,再买票
def task(mutex):
search()
# 买票过程要加锁
# 买前加锁
# mutex.acquire()
# buy() # 10个进程变成了串行执行
# # 买后释放锁
# mutex.release()
# 简化写法,因为Lock有__enter__与__exit__方法
with mutex:
buy()
# 如果不加锁,明明只有一张票,因为并发同时读取票,所以所有人都能买到
if __name__ == '__main__':
# 锁的创建,在哪?主进程创建锁
mutex = Lock() # 创建一把锁
# 模拟十个人买票(开10个进程)
for i in range(10):
t = Process(target=task, args=(mutex,))
t.start()
魔术方法:__enter__、__exit__
面向对象高级:魔法方法(双下划线包裹的),__enter__和__exit__,上下文管理器
案例:自己写一个类,实现类似于打开文件 with open 的功能
- 目标效果:
with MyClass('文件名','方式','编码') as f:
f.read()
在这写代码,f就关闭了
详细代码:
class MyClass():
def __init__(self,file_name,mode,encoding):
self.file_name=file_name
self.mode=mode
self.encoding=encoding
def __enter__(self):
print('只要有with,就会执行我')
self.file=open(self.file_name,self.mode,encoding=self.encoding)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
# 只要顶格写代码,就会执行我
print('只要顶格写代码,就会执行我')
self.file.close()
with MyClass('ticket','r','utf-8') as f:
print(f.read())
print('xxss')
print("sdfadasf")
a=MyClass('ticket','r','utf-8')
队列 Queue
队列:用于进程间通信的管道,所存放的值的队列
from multiprocessing import Queue
# 实例化得到要给对象
q=Queue(5) # 默认很大,可以放很多,写了个5,只能放5个
# 往管道中放值
q.put(1)
q.put('lqz')
q.put(18)
q.put(19)
q.put(20)
q.put(21) # 当存入队列中的值超过五个,这边的子程序会卡出,等对方接收值
q.put('阿巴阿巴',timeout=100) # 等100s对方还没接收值,就抛出异常含恨而终
q.put_nowait(asdf) # 队列满了,放不进去就不放了,报错
# 从管道中取值
# print(q.get())
# print(q.get())
# print(q.get()) # 当接收请求发出时队列里没有值,这边的子程序会卡出,等对方发送值
# print(q.get(timeout=100)) # 等100s还没有值,就抛出异常含恨而终
# print(q.get_nowait()) # 不等了,有就是有,没有就没有
print(q.empty()) # 看一下队列是不是空的
print(q.full()) # 看一下队列是不是满的
总结:
q=Queue(队列大小)
# 放值
q.put('阿巴阿巴')
q.put_nowait('阿巴阿巴') # 队列满了,放不进去就不放了,报错
# 取值
q.get() # 从队列头部取出一个值
q.get_nowait() # 从队列头部取值,没有就抛错
# 队列是否为空,是否满
print(q.empty()) # 看一下队列是不是空的
print(q.full()) # 看一下队列是不是满的
进程间通信-ipc
from multiprocessing import Process, Queue
import os
def task1(q):
print('我是task1进程,我的id号是:%s'%os.getpid())
q.put('lqz is handsome') # 往队列放入值
def task2(q):
res=q.get() # 从队列取出值
print('我是task2进程,我的id号是:%s'%os.getpid(),res)
if __name__ == '__main__':
q = Queue(5)
t1 = Process(target=task1, args=(q,))
t1.start()
t2 = Process(target=task2, args=(q,))
t2.start()
# print(q.get()) # 这一步会在进程t2创建前执行,将队列中的值取走,导致t2取不到值并卡住