锁,队列,事件,生产者消费者模型

6、锁(Lock)

(1)进程锁:Lock

​ lock.acquire():上锁

​ lock.release():解锁

​ 调用方式:from multiprocessing import Process

​ 注意点:

​ 同一时间只允许一个进程上一把锁(Lock锁的特点)

​ 优点:加锁可以保证多个进程修改同负一数据时,同一时间只能由一个任务执行修改,

​ 即串行修改,牺牲运行速度来保证数据安全

互斥锁Lock:互斥锁就是进程间互相排斥,谁先强盗资源,谁就先上锁改资源内容,保证数据的同步性

''' (1)基本使用 '''
# 创建一把锁
lock = Lock()
# 上锁
lock.acquire()
# 执行操作
# 解锁
lock.release()
# lock.acquire() 死锁阻塞.
print("执行程序 .... ")

''' (2) 模拟12306 抢票软件 '''

# 读取票数,更新票数
def wr_info(sign,dic=None):
	if sign == "r":
		with open("ticket",mode="r",encoding="utf-8") as fp:
			dic = json.load(fp)
		return dic
	elif sign == "w":
		with open("ticket",mode="w",encoding="utf-8") as fp:
			json.dump(dic,fp)
			
			
# 抢票方法
def get_ticket(person):
	# 获取数据库中实际数据
	dic = wr_info("r")

	# 模拟网络延迟
	time.sleep(0.5)	
	
	if dic["count"] > 0:
		print("%s抢到票了" % (person))
		dic["count"] -= 1
		# 更新数据库
		wr_info("w",dic)
	else:
		print("%s没有抢到这张票" % (person))
	

def run(person,lock):
	# 读取数据库中的实际票数
	dic = wr_info("r")
	print("%s 查询票数 : %s" % (person,dic["count"]))	


	# 上锁
	lock.acquire()
	# 抢票
	get_ticket(person)
	# 解锁
	lock.release()
	
if __name__ == "__main__":
	lock = Lock()
	lst =["刘思敏7","云超1","张恒2","尉翼麟3","王振4","黎建忠5","刘鑫炜6","李天兆8","魏小林9","李博10"]
	for i in lst:
		p = Process(target=run,args=(i,lock))
		p.start()

(2)信号量(Samephore)

​ lock.acquire():上锁

​ lock.release():解锁

​ 调用方式:from multiprocessing import Samephore

​ 注意点:

​ 多个锁一起上,不开锁,会造成死锁,上锁和解锁是一对

​ 优点:

​ Semaphore可以设置上锁数量;即同一时间允许多个进程上锁;

​ 创建进程的时候是异步的;执行任务的时候是同步的

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__":
	sem = Semaphore(4)
	for i in range(10):
		p = Process(target=ktv,args=("person%s" % (i),  sem))
		p.start()

7、事件(Event)

(1)阻塞事件

​ e = Event():生成事件对象

​ e.wait():动态给程序加阻塞,程序当中是否加阻塞取决于该对象中的is_set()

​ is_set:默认返回False

​ True是不加阻塞;False就是加阻塞

(2)控制属性的值

​ set()方法 将这个属性的值改成True

​ clear()方法 将这个属性的值改成False

​ is_set()方法 判断当前的属性是否为True (默认上来是False)

# (1) 基本使用
e = Event()
# 将阻塞事件中的值改成True
e.set()
print(e.is_set())
e.wait()
print("程序运行中1... ")

# 将阻塞事件中的值改成False
e.clear()
e.wait()
print("程序运行中2... ")

e = Event()
# 参数: 最多等待时间是5秒,过了5秒之后阻塞放行
e.wait()
print("程序运行中3... ")

# (2) 模拟红绿灯效果
import time,random
def traffic_light(e):
	print("红灯亮")
	while True:
		if e.is_set():
			# 绿灯状态,亮1秒钟
			time.sleep(1)
			print("红灯亮")
			# 把True => False
			e.clear()
		else:
			# 红灯状态,亮1秒钟
			time.sleep(1)
			print("绿灯亮")
			# 把False => True
			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(1,21):
		time.sleep(random.randrange(0,2)) # 0 1
		p2 = Process(target=car,args=(e,i))
		p2.start()
"""

# (3) 改造红绿灯 (在跑完小车之后,把红绿灯给我炸了)
if __name__ == "__main__":
	lst = []
	e = Event()
	p1 = Process(target=traffic_light,args=(e,))
	# 把红绿灯变成守护进程
	p1.daemon = True
	p1.start()

	# 开始创建小车
	for i in range(1,21):
		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("程序结束 ... ")

8、进程间通信(IPC)

IPC:IPC Inter-Process Communication

(1)实现进程之间通信的两种机制:

​ 管道 Pipe

​ 队列 Queue

(2)队列使用方式:from multiprocessing import Queue

​ put():存放

​ get():获取

​ get_nowait():拿不到报异常

​ 注:存在兼容性问题(windows好用 linux不好用 不推荐使用)

​ put_nowait():非阻塞版本的put

​ q.empty() 检测是否为空

​ q.full() 检测是否已经存满

''' (1) 基本使用 '''
q = Queue()
# 1.put 往队列中存值
q.put(111)
q.put(222)
q.put(333)
# 2.get 从队列中取值
res = q.get()
print(res)
res = q.get()
print(res)
res = q.get()
print(res)
# 3.队列里面没数据了,在调用get会发生阻塞
res = q.get()
print(res)

''' (2) 可以限定Queue队列的长度 '''
q1 = Queue(3)
q1.put(1)
q1.put(2)
q1.put(3)
# 超出了队列的长度,会发生阻塞
# q1.put(4)
# 如果列表满了,还往里面添加数据会直接报错.
q1.put_nowait(4)

''' (3)进程之间通过队列交换数据 '''
def func(q2):
	# 2.子进程取数据
	res = q2.get()
	print(res)
	# 3.子进程存数据
	q2.put("123")
	
	
if __name__ == "__main__":
	q2 = Queue()
	p = Process(target=func,args=(q2,))
	p.start()
	
	# 1.主进程添加数据
	q2.put("456")
	
	# 为了等待子进程把数据塞到队列中,在获取,要加一个join
	p.join()
	
	# 2.主进程获取数据
	res = q2.get()
	print("主程序执行结束:值为{}".format(res))

9、生产者与消费者

生产者 -----> 内存缓冲区 -----> 消费者

爬虫例子:

​ 1号进程负责抓取页面中的内容放到队列里
​ 2号进程负责把内容取出来,配合正则表达式,扣取关键字

​ 1号进程可以理解成生产者
​ 2号进程可以理解成消费者

​ 相对理想的生产者和消费者模型:
​ 追求彼此的速度相对均匀

从程序上来说:
生产者负责存储数据(put)
消费者负责获取数据(get)

(1)Queue

# (1)基本模型
from multiprocessing import Process,Queue
import random,time

# 消费者模型
def consumer(q,name):
	while True:
		food = q.get()
		time.sleep(random.uniform(0.1,1))
		print("%s 吃了一个%s" % (name,food))
	
	
# 生产者模型
def producer(q,name,food):
	for i in range(5):
		time.sleep(random.uniform(0.1,1))
		print("%s 生产了 %s%s" % (name,food,i))
		q.put(food+str(i))
	
	
if __name__ == "__main__":
	q = Queue()
	# 消费者1
	p1 = Process(target=consumer,args=(q,"张恒"))
	p1.start()

	# 生产者1
	p2 = Process(target=producer,args=(q,"尉翼麟","黄金"))
	p2.start()	

	
# (2)优化模型

# 消费者模型
def consumer(q,name):
	while True:
		food = q.get()
		if food is None:
			break
		time.sleep(random.uniform(0.1,1))
		print("%s 吃了一个%s" % (name,food))
	
	
# 生产者模型
def producer(q,name,food):
	for i in range(5):
		time.sleep(random.uniform(0.1,1))
		print("%s 生产了 %s%s" % (name,food,i))
		q.put(food+str(i))
	
	
if __name__ == "__main__":
	q = Queue()
	# 消费者1
	p1 = Process(target=consumer,args=(q,"张恒"))
	p1.start()
	# 消费者2
	a2 = Process(target=consumer,args=(q,"云超"))
	a2.start()
	
	# 生产者1
	p2 = Process(target=producer,args=(q,"尉翼麟","黄金"))
	p2.start()
	
	# 生产者2
	b2 = Process(target=producer,args=(q,"刘思敏","钻石"))
	b2.start()
	
	# 在生产完所有的数据之后,在队列的末尾塞入一个None
	p2.join()
	b2.join()
	# 消费者模型如果获取的是None,代表停止消费
	q.put(None)
	q.put(None)

(2)JoinableQueue

​ put 存储
​ get 获取
​ task_done 队列计数减1
​ join 阻塞

​ 使用方式:from multiprocessing import JoinableQueue

​ task_done 配合 join 一起使用
​ put 一次 每存放一个值,队列计数器加1
​ get 一次 通过task_done让队列计数器减1
​ join 函数,会根据队列中的计数器来判定是阻塞还是放行
​ 如果计数器变量是0,意味着放行,其他情况阻塞;

# (1) 基本使用
jq = JoinableQueue()
# put 会让队列计数器+1
jq.put("a")
print(jq.get())
# 通过task_done,让队列计数器-1
jq.task_done()
# 只有队列计数器是0的时,才会放行
jq.join() # 队列.join
print("finish")

# (2) 改造生产者消费者模型
import random,time

# 消费者模型
def consumer(q,name):
	while True:
		food = q.get()		
		time.sleep(random.uniform(0.1,1))
		print("%s 吃了一个%s" % (name,food))
		q.task_done()
	
# 生产者模型
def producer(q,name,food):
	for i in range(5):
		time.sleep(random.uniform(0.1,1))
		print("%s 生产了 %s%s" % (name,food,i))
		q.put(food+str(i))
	
	
if __name__ == "__main__":
	q = JoinableQueue()
	# 消费者1
	p1 = Process(target=consumer,args=(q,"张恒"))
	p1.daemon = True
	p1.start()

	# 生产者1
	p2 = Process(target=producer,args=(q,"尉翼麟","黄金"))
	p2.start()	
	
	# 把生产者所有的数据都装载到队列中
	p2.join()
	
	# 当队列计数器减到0的时候,会立刻放行
	# 必须等待消费者模型中所有的数据都task_done之后,变成0了就代表消费结束.
	q.join()
	
	print("程序结束....")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值