并发编程(3)
1. 优化抢票程序
from multiprocessing import Process,Lock
import json,time,os
def search():
time.sleep(1) # 模拟网络io
with open('db','rt',encoding='utf-8') as f:
res = json.load(f)
print(f'还剩{res["count"]}')
def get():
with open('db','rt',encoding='utf-8') as f:
res = json.load(f)
time.sleep(1) # 模拟网络io
if res['count'] > 0:
res['count'] -= 1
with open('db','wt',encoding='utf-8') as f:
json.dump(res,f)
print(f'进程{os.getpid()}抢票成功')
time.sleep(1.5) # 模拟io
else:
print('票已售空!!!')
def task(lock):
search()
# 锁住
lock.acquire()
get()
lock.release()
# 释放锁头
if __name__ == '__main__':
lock = Lock() # 写在主进程是为了让子进程拿到同一把锁
for i in range(15):
p = Process(target=task,args=(lock,))
p.start()
# p.join()
# 进程锁 是把锁住的代码变成串行
# join 是把所有的子进程变成了串行
#加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。
2. 队列
进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的
# ipc机制 进程通讯
# 管道:pipe 基于共享的内存空间
# 队列:pipe + 锁 queue
from multiprocessing import Process,Queue
# 案例一
q = Queue()
q.put('aa') # q.put()方法用以插入数据到队列中
q.put([1,2,4])
q.put(2)
print(q.get()) # q.get()方法可以从队列读取并且删除一个元素
print(q.get())
print(q.get())
q.put(5)
q.put(5)
print(q.get()) # 默认就会一直等着拿值
# 案例二
q = Queue(4)
q.put('aa')
q.put([1,2,4])
q.put([1,2,4])
q.put(2)
q.put('cc') # 队列满了的情况再放值,会阻塞
# 案例三(从这往下都是了解)
q = Queue(3)
q.put('zhao',block=True,timeout=2)
q.put('zhao',block=True,timeout=2)
q.put('zhao',block=True,timeout=2)
q.put('zhao',block=True,timeout=5) # put里的 block=True 如果满了会等待,timeout最多等待n s,如果n s队列还是满的就报错
# 案例四
q = Queue()
q.put('yyyy')
q.get()
q.get(block=True,timeout=5) # block=True 阻塞等待,timeout最多等5s,如果5s队列还是满的就报错
# 案例五
q = Queue(3)
q.put('qwe')
q.put('qwe')
q.put('qwe')
q.put('qwe',block=False) # 对于put来说block=False 如果队列满了就直接报错
q = Queue(3)
q.put('qwe')
q.get()
q.get(block=False)
# block = False 拿不到不阻塞,直接报错
# blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
# 案例六
q = Queue(1)
q.put('123')
q.get()
q.put_nowait('666') # block = False
q.get_nowait() # block = False
3. 生产者消费者模型
生产者消费者模型是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
'''
生产者:生产数据的任务
消费者:处理数据的任务
生产者--队列(盆)--》消费者
生产者可以不停的生产,达到了自己最大的生产效率,消费者可以不停的消费,也达到了自己最大的消费效率
生产者消费者模型大大提高了生产者生产的效率和消费者消费的效率
# 补充:queue不适合传大文件,通常传一些消息
'''
# 基于队列实现生产者消费者模型
# 生产者发送结束信号
from multiprocessing import Process,Queue
def producer(q,name,food):
'''生产者'''
for i in range(10):
print(f'{name}生产了{food}{i}')
res = f'{food}{i}'
q.put(res)
q.put(None) # 发送结束信号
def consumer(q,name):
'''消费者'''
while True:
res = q.get(timeout=5)
if res is None: # 收到结束信号则结束
break
print(f'{name}吃了{res}')
if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer,args=(q,'bob','包子'))
c1 = Process(target=consumer,args=(q,'aa'))
p1.start()
c1.start()
# 主进程发送结束信号
from multiprocessing import Process,Queue
import time,random
def producer(q,name,food):
'''生产者'''
for i in range(3):
print(f'{name}生产了{food}{i}')
time.sleep(random.randint(1,3))
res = f'{food}{i}'
q.put(res)
def consumer(q,name):
'''消费者'''
while True:
res = q.get(timeout=5)
if res is None:
break
time.sleep(random.randint(1,3))
print(f'{name}吃了{res}')
if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer,args=(q,'rocky','包子'))
p2 = Process(target=producer,args=(q,'mac','韭菜'))
p3 = Process(target=producer,args=(q,'nick','蒜泥'))
c1 = Process(target=consumer,args=(q,'aa'))
c2 = Process(target=consumer,args=(q,'bb'))
p1.start()
p2.start()
p3.start()
c1.start()
c2.start()
p1.join()
p2.join()
p3.join() # 生产者生产完毕
q.put(None)
q.put(None) # 几个消费者put几次
# 主进程需要等生产者结束后才应该发送该信号
# JoinableQueue
from multiprocessing import Process,Queue,JoinableQueue
import time,random
def producer(q,name,food):
'''生产者'''
for i in range(3):
print(f'{name}生产了{food}{i}')
time.sleep(random.randint(1,3))
res = f'{food}{i}'
q.put(res)
def consumer(q,name):
'''消费者'''
while True:
res = q.get()
time.sleep(random.randint(1,3))
print(f'{name}吃了{res}')
q.task_done()
if __name__ == '__main__':
q = JoinableQueue()
p1 = Process(target=producer,args=(q,'rocky','包子'))
p2 = Process(target=producer,args=(q,'mac','韭菜'))
p3 = Process(target=producer,args=(q,'nick','蒜泥'))
c1 = Process(target=consumer,args=(q,'aa'))
c2 = Process(target=consumer,args=(q,'bb'))
p1.start()
p2.start()
p3.start()
c1.daemon = True
c2.daemon = True
c1.start()
c2.start()
p1.join()
p2.join()
p3.join() # 生产者生产完毕
q.join() # 分析
# 生产者生产完毕--这是主进程最后一行代码结束--q.join()消费者已经取干净了,没有存在的意义了
# 这是主进程最后一行代码结束,消费者已经取干净了,没有存在的意义了,守护进程的概念
4. 测试JoinableQueue
from multiprocessing import Process,Queue,JoinableQueue
q = JoinableQueue()
q.put('zhao') # 放队列里一个任务
q.put('qian')
print(q.get())
q.task_done() # 完成了一次任务 #向q.join()发送一次信号,证明一个数据已经被取走了
print(q.get())
q.task_done() # 完成了一次任务
q.join() # 计数器不为0的时候 阻塞等待计数器为0后通过
# q.join():生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法为止
# 想象成一个计数器:put + 1 task_done - 1
5. 初步识别线程
'''
初识别线程
在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程
在工厂中,每个车间都有房子,而且每个车间默认就有一条流水线
操作系统 ===》工厂
进程 ===》车间
线程 ===》流水线
cpu ===》电源
线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程
车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线
流水线的工作需要电源,电源就相当于cpu
线程:cpu最小的执行单位
进程:资源集合/资源单位
线程运行 = 运行代码
进程运行 = 各种资源 + 线程
进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。
多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。
'''