一 锁 lock
为了更好的理解概念,我们来模拟12306抢票软件, 多进程\多线程
from multiprocessing import Process
import json # 读文件
import time
# 写一个文件模拟数据库,ticket
"""
{"count":1}
"""
# 读取票数\更新票数,一个函数实现了两个功能,需要sign来进行判断到底进行那个功
def wr_info(sign,dic=None):
if sign == "r":
# 读取文件
with open("ticket",mode="r",encodeing="utf-8") as fp:
dic = json.load(fp)
return dic
elif sign == "w":
# 再加一个参数
with open("ticket",mode="w",encodeing="utf-8") as fp:
json.dump(dic,fp)
# 抢票方法
def get_ticket(person): # 谁在抢票,需要个人的姓名
# 读取数据库中的实际票数
dic = wr_info("r") # 得到一个字典,给一个r模式
time.sleep(0.5)
if dic["count"] > 0:
print("%s抢到票了" % (person))
# 更新数据库
dic["count"] -= 1
wr_info("w",dic) # 把新字典更新进去,下次再查票数-1
else:
print("%s 没有抢到这个票" % (person))
def run(person,lock):
# 读取数据库中的实际票数
dic = wr_info("r") # 得到一个字典,给一个r模式
print("%s在查票,还剩%s张" % (person,dic["count"]))
# 上锁
lock.acquire()
# 抢票
get_ticket(person)
# 解锁
lock.release()
if __name__ == "__main__"
lock = Lock()
# 这10个人在抢票,每个人都是一个单独的进程
lst = ["alex1","xboy2","aa3","bb4","cc5","dd6","ee7","ff8","gg9","hh10"]
for i in lst:
p = Process(target=run,args=(i,lock))
p.start() # 这里10个人都显示抢到票了,这在现实中是不可能的,属于逻辑错误
get_ticket("alex")
上面的代码没有考虑网络延迟,实际情况中,因为网络延迟,我们很有可能抢不到票,我们
模拟一下网络延迟,使用time.sleep
这个时候,锁的概念应运而生,那么我们应该在哪里上锁呢?
只上锁不解锁,程序会发生阻塞,下面的程序就不执行了
"""
总结:区分同步和异步
当创建10个继承的时候是异步的,直到查完票数截止;
当执行get_ticket方法的时候,各进程之间是同步的"""
1.锁的基本语法
什么是死锁现象?只上锁不解锁,程序发生阻塞,下面的程序不执行了
"""
互斥锁:进程之间的互相排斥,谁抢到了资源谁先试用,后抢到资源的后试用
"""
from multiprocessing import Process,Lock
import json # 读文件
import time
1.1创建一把锁
lock = Lock()
1.2上锁
lock.acquire()
1.3执行操作
1.4解锁
lock.release()
2.信号量 semaphore 本质上就是锁,可以控制上锁的数量
"""
sem = Semaphore(4)
sem.acquire()
# 执行相应操作
sem.release()
"""
from multiprocessing import Semaphore,Process
import time,random
def ktv(person,sem):
sem.acquire()
# 开始唱歌
print("%s进入ktv,正在唱歌" % (person))
time.sleep(random.randrange(3,7)) # 3,4,5,6
print("%s离开ktv,唱完了" % (person))
sem.release()
if __name__ == "__main__":
# 限制4把锁,有进程结束之后,下一个进程再进入,始终保持有四个进程在运行
sem = Semaphore(4)
for i in range(10):
Process(target=ktv,args=("person%s" % (i),sem))
p.start()
二 事件 Event
e = Event() 生成事件对象e
e.wait() 动态给程序加阻塞
程序当中是否加阻塞完全取决于该对象中的is_set(),默认返回值是False
True 不加阻塞 False 加阻塞
set() 将这个属性的值改为True
clear() 将这个属性的值改成False
is_set() 判断当前的属性是否为True
1.基本语法
from multiprocessing import Process,Event
# e = Event()
# print(e.is_set())
e = Event()
e.wait(2) # 参数代表最大等待时间, 最多等待两秒,之后阻塞放行
print("程序运行中3")
# Lock\Semaphore\Event数据隔离,但是可以通过socket发消息
2.模拟红绿灯效果
import time,random
def traffic_light():
print("红灯亮")
while True:
if e.is_set():
# 让绿灯状态,亮1秒钟
time.sleep(1)
print("红灯亮")
# 把True => False
e.clear()
else:
# 红灯状态,亮1秒钟
time.sleep(1)
print("绿灯亮")
e.set()
# e = Event()
# traffic_light(e)
def car(e,i):
if not e.is_set():
# 走到这个分支里面来,一定是红灯状态,小车停
print("car%s在等待" % (i))
e.wait() # 阻塞, 加一个等待
print("car%s通行了" % (i))
if __name__ == "__main__":
e = Event
p1 = Process(target=traffic_light,args=(e,))
p1.start()
# 开始创建小车
for i in range(20):
time.sleep(random.randrange(0,2)) # 0,1
p2 = Process(target=car,args=(e,i))
p2.start()
一共有多少进程??22
3.改造红绿灯,在跑完小车后,把红绿灯给我炸了(守护进程)
if __name__ == "__main__":
lst = []
e = Event()
p1 = Process(target=traffic_light,args=(e,))
p1.daemon = True
p1.start()
# 开始创建小车
for i in range(20):
time.sleep(random.randrange(0,2)) # 0,1
p2 = Process(target=car,args=(e,i))
p2.start()
lst.append(p2)
# 让所有的小车都通过之后,再终止交通灯
for i in lst:
i.join()
print("程序结束..")
通过socket内部通讯
三 进程队列 Queue
from multiprocessing import Process,Queue
# 先进先出,后进后厨
1.基本语法
q = Queue()
"""
put 往队列中存值
get 从队列中取值
get_nowait() 没值报错,但有兼容性问题,在win里没问题,但是在linux里面会报错,估计以后会修复
"""
q.put(111)
q.put(222)
q.put(333)
res = q.get()
print(res) # 111
res = q.get()
print(res) # 222
res = q.get()
print(res) # 333
队列里面没有数据了,再调用get会发生阻塞
没有值取出来的时候不会报错,它会等着后面的进程继续往里面塞值,等有值了再取
res = q.get_nowait()
print(res) # 这个会报错, Empty
那么我们应该怎么抑制报错呢? try except 就来了
try:
res = q.get_nowait()
print(res)
except:
pass
"""Queue 进程里的 queue 线程里的"""
我们可以限定Queue队列的长度
q1 = Queue(3)
q1.put(1)
q1.put(2)
q1.put(3)
# 超出队列的长度,会发生阻塞,有没有一种可能,超了长度不阻塞直接报错呢?有,put_nowait(4)
q1.put(4)
print(q1)
# 这个报错也可以用try except来抑制报错
q1.put_nowait()
3.进程之间可以通过队列交换数据
def func(q2):
# 2.子进程取数据
res = q2.get()
print(res) # wangzhen
# 3.子进程存数据
q2.put("liusimin")
if __name__ == "__main__":
# 让主进程往里面塞,子进程往外面取
q2 = Queue()
p = Process(target=func,args=(q2,))
p.start()
# 1.主进程添加数据
q2.put("wangzhen")
# 为了等待子进程把数据塞到队列中,再获取,这里加一个join
p.join()
# 4. 主进程获取数据
res = q2.get() # 主进程快
print(res)
print("主程序执行结果,值为{}".format(res))
四 生产者消费者模型
"""
爬虫例子:
1号进程负责抓取页面中的内容放到队列里
2号进程负责把内容取出来,配合正则表达式抽取关键字
1号进程可以理解成生产者
2号进程可以理解成消费者
相对理想的生产者和消费者模型:追求彼此的速度相对均匀
从程序上来讲,生产者负责存储数据put,消费者负责获取数据get
"""
1.基本模型
from multiprocessing import Process,Queue
import time
import random
# 消费者模型
def consumer(q,name):
while True:
food = q.get()
if food is None:
break
time.sleep(random.uniform(0.1,1))
print("%s eat %s" % (name,food))
# 生产者模型
def producer(q,name,food):
for i in range(5):
time.sleep(random.uniform(0.1,1)) # uniform 随机小数
print("%s 生产了 %s%s" % (name,food,i))
q.put(food+str(i))
if __name__ == "__main__":
q = Queue()
# 消费者1
p1 = Process(target=consumer,args=(q,"alex"))
p1.start()
# 消费者2
a2 = Process(target=consumer,args=(q,"yunchao"))
a2.start()
# 生产者1
p2 = Process(target=producer,args=(q,"xboy","gold"))
p2.start()
# 生产者2
b2 = Process(target=producer,args=(q,"liusimin","zuanshi"))
b2.start()
# 再生产完所有的数据之后,在队列的末尾塞入一个None
p2.join()
b2.join()
# 消费者模型如果获取的是None,代表停止消费
q.put(None)
q.put(None)
# 如果有一堆消费者,这里是不是也要塞一堆None?
print("程序执行结束...")
2.优化模型
joinableQueue
"""
put 存储
get 获取
task_done 队列计数减1
join 阻塞
task_done 配合 join 一起使用 他是个蛋
put一次,每存放一个值,队列计数加1
get一次,队列计数通过task_done 减1
join函数,会根据队列中的计数器来判定是阻塞还是放行
如果计数器变量为0,意味着放行;如果是其他情况,阻塞
"""
from multiprocessing import Process,joinableQueue
1.基本使用
jq = joinableQueue()
jq.put("a") # put使队列计数器 +1
print(jq.get())
jq.task_done() # 通过task_done让队列计数器-1
jq.join() # 只有队列计数器是0的时候,才会放行 队列.join
print("finish")
# 优化生产者消费者模型
from multiprocessing import Process,Queue
import time
import random
# 消费者模型
def consumer(q,name):
while True:
food = q.get()
time.sleep(random.uniform(0.1,1))
print("%s eat %s" % (name,food))
q.task_done()
# 生产者模型
def producer(q,name,food):
for i in range(5):
time.sleep(random.uniform(0.1,1)) # uniform 随机小数
print("%s 生产了 %s%s" % (name,food,i))
q.put(food+str(i))
if __name__ == "__main__":
q = joinableQueue()
# 消费者1
p1 = Process(target=consumer,args=(q,"alex"))
p1.start()
# 生产者1
p2 = Process(target=producer,args=(q,"xboy","gold"))
p2.start()
# 把生产者所有的数据都装在到队列中
p2.join()
# 当队列计数器减到0的时候,会立刻放行
# 必须等待消费者模型中所有的数据都task_done后,变成0了代表消费结束
q.join()
print("程序执行结束...")
python_learn Lock、Semaphore、Event、队列及生产者消费者模型
最新推荐文章于 2022-09-23 01:36:08 发布