首页 > 技术文章 > 5.进程 -锁

jia-shu 原文

锁 - 事件 -IPC进程通信-生产者消费者

1.进程锁

(1) Lock : 同一时间,允许一个进程上一把锁
# 1.锁 lock
"""
同一时间,允许一个进程上一把锁,就是lock
创建锁对象 : lock = Lock()
上锁和解锁是成对出现:
    1.上锁 : lock.acquire()
    2.解锁 : lock.release()
"""
from multiprocessing import Lock,Process
import json,random,time

###12306抢票
# 1.读写数据票数
def wr_info(sign,dic=None):
    if sign == "r": #读取票数
        with open("data.txt",mode = "r",encoding="utf-8") as fp:
            dic = json.load(fp)
            return dic
    elif sign == "w": #写入票数
        with open("data.txt",mode="w",encoding="utf-8") as fp:
            json.dump(dic,fp)

# 2.执行抢票方案
def get_ticket(person):
    dic = wr_info("r") #获取数据
    time.sleep(random.uniform(0,1)) #模拟网络延迟
    if dic["amount"] > 0: 
        print("恭喜~{}抢票成功".format(person))
        dic["amount"] -= 1 #抢票成功后,总票数减一
        wr_info("w",dic) #更新票数
    else:
        print("{}抢票失败~".format(person))

def main(person,lock):
    dic = wr_info("r")
    print("{}查看剩余票数为{}张".format(person,dic["amount"]))

    lock.acquire() #上锁 确保同一时间只有一个进程进来抢票
    get_ticket(person)
    lock.release() #解锁 别的进程才有机会进来抢票

if __name__ == "__main__":
    lock = Lock() #创建锁对象
    lst = ["贾英贺","熊大","熊二","光头强","吉吉","涂涂"]
    for i in lst:
        p = Process(target=main,args=(i,lock))
        p.start()

"""
创建进程时,是异步程序
当上锁是,变成了同步程序
(2) 信号量Semaphore : 同一时间允许多个程序上n把锁
# 信号量Semaphore
"""
sem = Semaphore(n)
同一时间允许多个程序上n把锁
sem.acquire() #上锁
sem.release() # 解锁
"""
from multiprocessing import Semaphore,Process
import time,random

# 去歌房唱歌
def sing(person,sem):
    sem.acquire()
    print("{}进入歌房唱歌~".format(person))
    time.sleep(random.randrange(2,8))
    print("{}离开了~~".format(person))
    sem.release()

if __name__ == "__main__":
    sem = Semaphore(3) #同一时间3个进程上锁
    lst = [11,22,33,44,55,66,77,88,99]
    for i in lst:
        p = Process(target=sing,args=(i,sem))
        p.start()

2.事件 Event

# 事件 Event
"""
阻塞事件:
    e = Event() 生成事件对象
    e.wait() 动态给程序加阻塞,程序中是否加阻塞完全取决于该对象中的is_set()
        如果是True 不加阻塞
        如果是False 加阻塞
控制这个属性的值:
    set() 将属性值改为True
    clear() 将属性值改为False
    is_set() 判断当前属性是否为True (默认是False)
"""
from multiprocessing import Process,Event
import time,random

# 1.语法一
"""
e = Event() #生成事件对象
print(e.is_set())
e.wait()
print("代码执行了~") #阻塞,不能打印
"""
# 2.语法二
e = Event()
e.set() #将属性值改为True
e.wait()
print("代码执行了1~") #不阻塞,可以打印

# 3.语法三 阻塞几秒后放行
e = Event()
e.wait(1) #阻塞1秒后执行下面代码
print("代码执行了2~")

# 4.模拟红绿灯效果
#模拟红绿灯
def traffic_light(e): 
    print("红灯亮")
    while True:
        if e.is_set(): #True绿灯过来
            time.sleep(1)
            print("红灯亮")
            e.clear() #改成False 变红灯
        else:       #红灯过来
            time.sleep(1)
            print("绿灯亮")
            e.set() #改成True 变绿灯
            
# 模拟小车
def car(e,i):
    if not e.is_set(): #当红灯时,小车阻塞
        print("当前小车{}需等待~".format(i))
        e.wait() #小车阻塞
    # 当绿灯时小车放行    
    print("当前小车{}放行了~".format(i))

# 全国交通灯
"""
if __name__ == "__main__":
    e = Event()
    p1 = Process(target=traffic_light,args=(e,)) #创建交通灯进程
    p1.start()

    for i in range(20):
        time.sleep(random.randrange(3))
        p2 = Process(target=car,args=(e,i))
        p2.start()
"""
# 包头交通灯(车跑完后,交通灯关闭,省电)
if __name__ == "__main__":
    e = Event()
    p1 = Process(target=traffic_light,args=(e,))
    p1.daemon = True
    p1.start()

    lst = []
    for i in range(1,21):
        time.sleep(random.randrange(2))
        p2 = Process(target=car,args=(e,i))
        lst.append(p2)
        p2.start()
    for i in lst:
        i.join()
    print("关闭交通灯")

3.进程队列 IPC进程通信

(1) Queue
# 进程间通信IPC (Inter-Process Communication)
"""
实现进程间通信两种机制:
    1.管道 Pipe
    2.队列 Queue
q.put() 存放数据
q.get() 获取数据 
get_nowait() 拿不到报异常
put_nowait() 数据存满再存报错
q.empty()  检测是否为空
q.full()  检测是否存满
"""

# 进程队列
from multiprocessing import Queue,Process
"""
先进先出,后进后出
q = Queue()  生成队列对象
q = Queue(n) 生成可以存n个数据队列对象
"""

# 1.基本语法
q = Queue() #生成队列对象
q.put(1) #存放数据
q.put(2)
print(q.get()) # 获取数据 1
print(q.get()) # 2
# print(q.get()) #获取不到数据阻塞

# 2.进程间IPC通信
def func(q):
    res = q.get() #2.子进程获取主进程存放的数据
    print(res,"<子进程>")
    q.put("贾英贺") #3.子进程存放数据

if __name__ == "__main__":
    q = Queue()
    p = Process(target=func,args=(q,))
    p.start()
    q.put("光头强") #1.主进程存储数据
    p.join() #为了让主进程获取数据,必须等待子进程执行完毕后,在向下执行
    res = q.get()
    print(res,"<主进程>")
"""
光头强 <子进程>
贾英贺 <主进程>
"""
(2) Queue 生产消费模型
# 生产者消费者模型
"""
从程序上看:
    生产者负责储存数据(put)
    消费者负责获取数据(get)
理想的生产者消费者模型:
    1.生产多少,消费多少
    2.生产数据的速度与消费数据的速度相对一致
"""
from multiprocessing import Queue,Process
import time,random

# 1.基础版
def producer(q,name,food): #生产者
    for i in range(1,5):
        time.sleep(random.random())
        res = "第{}碗{}".format(i,food)
        print("{}生产了{}".format(name,res))
        q.put(res) #储存数据

def consumer(q,name): #消费者
    while True:
        time.sleep(random.random())
        res = q.get() #获取数据
        print("{}吃了{}".format(name,res))

if __name__ == "__main__":
    q = Queue()
    p1 = Process(target=producer,args=(q,"贾英贺","粥"))
    p2 = Process(target=consumer,args=(q,"光头强"))
    p1.start()
    p2.start()
缺点 : 循环没断
(3) JoinableQueue 生产消费模型
# JoinableQueue 队列
"""
put 存放一次数据 计数器属性值 +1
get 获取 
task_done 一次数据, 计数器属性值 -1
当计数器值=0时
    队列.join() 放行
当计数器值非0时
    队列.join() 阻塞
"""
# 对生产者消费者模型进行改造
from multiprocessing import JoinableQueue,Process
import time,random

# 生产者
def producer(q,name,food):
    for i in range(1,5):
        time.sleep(random.random())
        res = "第{}碗{}".format(i,food)
        print("{}生产{}".format(name,res))
        q.put(res) #存储数据,内置计数器+1

# 消费者
def consumer(q,name):
    while True:
        time.sleep(random.random())
        food = q.get() #获取数据
        print("{}吃了{}".format(name,food))
        q.task_done() #内置计数器-1

if __name__ == "__main__":
    jq = JoinableQueue()
    p1 = Process(target = producer, args=(jq,"光拖欠","小米粥"))
    p2 = Process(target= consumer, args = (jq,"熊大"))
    p2.daemon = True #守护消费者进程
    p1.start()
    p2.start()
    p1.join() #必须等到生产者全部生产完毕,在放行
    jq.join() #必须让消费者全部吃完
    print("主进程结束")

4.Maneger (list,dict) 多进程之间共享列表,字典

# Manager(list,dict)
from multiprocessing import Process,Manager,Lock

def work(data,lock):
    with lock: # 上锁解锁自动完成
        data[0] -=1
        
if __name__=="__main__":
    lst = []
    lock = Lock()
    m = Manager()
    # 创建一个多进程之间共享数据的字典
	# data = m.dict( {"count":0} )
    # 创建一个多进程之间共享数据的列表
    data = m.list([111,222])
    for i in range(10):
        p = Process(target  = work ,args = (data,lock))
        p.start()
        lst.append(p)
    for i in lst:
        i.join()
    print(data)#[101, 222]

推荐阅读