目录python
经过以前的学习,咱们想方设法的实现了程序的异步,让多个任务能够同时在几个进程中并发处理json
他们之间的运行没有顺序,一旦开启也不受咱们的控制,随之而来的带来了新的问题:安全
当多个进程使用同一份数据资源的时候,就会引发数据安全或数据混乱问题服务器
# coding=utf-8 import os import time import random from multiprocessing import Process,Lock def work(n): print('%s: %s is running' %(n,os.getpid())) time.sleep(random.random()) print('%s:%s is done' %(n,os.getpid())) def run(n,lock): lock.acquire() work(n) lock.release() if __name__ == '__main__': lock = Lock() for i in range(3): p=Process(target=run,args=(i,lock)) p.start() 2: 6616 is running 2:6616 is done 0: 7052 is running 0:7052 is done 1: 10752 is running 1:10752 is done
上面状况使用加锁实现顺序执行,程序由并发执行变成了串行执行。网络
以模拟抢票软件为例,验证数据的重要性数据结构
data 文件内容:{"count": 0} # coding=utf-8 from multiprocessing import Process,Lock import json import time # 查看余票 def search(user): # 打开data文件查看余票 with open("data","r",encoding="utf-8") as f: dic = json.load(f) print(f"{user} 查看余票,还剩{dic.get('count')}") # 买票 def buy(user): # 先打开文件获取车票数据 with open("data","r",encoding="utf-8") as f: dic = json.load(f) # 模拟网络延迟 time.sleep(1) # 如有票修改data数据 if dic.get("count") > 0: dic["count"] -= 1 with open("data","w",encoding="utf-8") as f: json.dump(dic,f) print(f"{user} 抢到了车票!") else: print(f"{user} 抢票失败") def run(user,lock): search(user) # 锁住 lock.acquire() buy(user) # 释放锁 lock.release() # 给buy函数添加锁,让并发变成了串行 # 牺牲了执行效率,但保证了数据的安全 # 在程序并发执行时,须要修改数据时使用互斥锁 if __name__ == '__main__': # 建立一把锁 lock = Lock() # 模拟并发10个客户端抢票 for i in range(10): p = Process(target=run,args=(f"服务器 {i} ",lock)) p.start() 服务器 2 查看余票,还剩1 服务器 3 查看余票,还剩1 服务器 7 查看余票,还剩1 服务器 6 查看余票,还剩1 服务器 2 抢到了车票! 服务器 0 查看余票,还剩0 服务器 8 查看余票,还剩0 服务器 4 查看余票,还剩0 服务器 1 查看余票,还剩0 服务器 9 查看余票,还剩0 服务器 5 查看余票,还剩0 服务器 3 抢票失败 服务器 7 抢票失败 服务器 6 抢票失败 服务器 0 抢票失败 服务器 8 抢票失败 服务器 4 抢票失败 服务器 1 抢票失败 服务器 9 抢票失败 服务器 5 抢票失败
加锁能够保证多个进程在修改同一块数据的时候,同一时间只能有一个任务能够进行修改,即串行的修改,这样速度是慢了,可是保证了数据的安全。并发
至关于在内存中产生一个队列空间,能够存放多个数据,遵循先进先出原则dom
# coding=utf-8 from multiprocessing import Queue ''' Queue 用法: ''' # 建立出q对象,Queue类能够传参数(), # 5表示能够存5个值 # 若不填能够存无限个值 q = Queue(5) # 添加数据 q.put("1") q.put("2") q.put("3") q.put("4") q.put("5") # 查看队列中是否满了 True print(q.full()) # 继续添加数据,若队列满了就报错 queue.Full q.put_nowait("6") # 获取数据,遵循先进先出,,若队列中无数据可取,也会卡主 print(q.get()) print(q.get()) print(q.get()) print(q.get()) print(q.get()) # 判断队列是否为空 True print(q.empty()) # 继续获取数据,若队列中没有数据了就报错 queue.Empty print(q.get_nowait())
堆栈是一个后进先出的数据结构,工做方式就像一对汽车排队进去一个死胡同里面,最早进去的必定是最后出来异步
进程间数据时相互隔离的,若想实现进程间通讯,能够利用队列。函数
# coding=utf-8 from multiprocessing import Process from multiprocessing import Queue def test1(q): data = "数据" # 添加队列数据 q.put(data) print("子进程1开始添加数据到队列中") def test2(q): # 获取队列数据 data = q.get() print(f"子进程2从队列中获取数据:{data}") if __name__ == '__main__': q = Queue() p1 = Process(target=test1,args=(q,)) p2 = Process(target=test2,args=(q,)) p1.start() p2.start() print("主程序")
生产者:生产数据的
消费者:使用数据的
生活中:好比卖油条,一边生产油条,一边买油条,供需不平衡
程序中:经过队列,生产者把数据添加队列中,消费者从队列中获取数据
# coding=utf-8 from multiprocessing import Process,Queue import time # 生产者 def producer(name,food,q): for i in range(10): data = food,i print(f"用户{name} 制做了{data}") q.put(data) time.sleep(0.1) # 消费者 def consumer(name,q): while True: # time.sleep(1) data = q.get() if not data: break print(f"用户{name} 开始吃{data}") if __name__ == '__main__': q = Queue() # 创造生产者 p1 = Process(target=producer,args=("qinyj","油条",q)) # 创造消费者 c1 = Process(target=consumer,args=("qinyj",q)) p1.start() # 添加消费者守护进程,表示跟着主程序一块儿停掉,即生产者不生产了,消费者也不吃了 c1.daemon = True c1.start() # 添加join方法,生产者生产完毕以后就停掉,而后消费者也会一直吃 p1.join() # time.sleep(10) # 添加延迟,等待生产者生产完毕把全部进程死掉 print("主程序") 用户qinyj 制做了('油条', 0) 用户qinyj 开始吃('油条', 0) 用户qinyj 制做了('油条', 1) 用户qinyj 开始吃('油条', 1) 用户qinyj 制做了('油条', 2) 用户qinyj 开始吃('油条', 2) 用户qinyj 制做了('油条', 3) 用户qinyj 开始吃('油条', 3) 用户qinyj 制做了('油条', 4) 用户qinyj 开始吃('油条', 4) 用户qinyj 制做了('油条', 5) 用户qinyj 开始吃('油条', 5) 用户qinyj 制做了('油条', 6) 用户qinyj 开始吃('油条', 6) 用户qinyj 制做了('油条', 7) 用户qinyj 开始吃('油条', 7) 用户qinyj 制做了('油条', 8) 用户qinyj 开始吃('油条', 8) 用户qinyj 制做了('油条', 9) 用户qinyj 开始吃('油条', 9) 主程序