• 互斥锁,多进程之间的通信


    1. 互斥锁

    ​ 当多个进程抢占同一数据时,将数据加锁,使进程按串行的方式去获取数据,先到先得,保证了公平、数据的安全。

    ​ lock.acquire() # 加锁

    ​ lock.release() # 释放

    ​ 死锁:连续lock.acquice() 多次,会阻塞进程。

    # 模拟三个用户使用同一个打印机打印。
    from multiprocessing import Process
    from multiprocessing import Lock	# 导入互斥锁
    import os
    import time
    import random
    import sys
    
    def task1(lock):
    
        lock.acquire()		# 加锁
        print(f'task1-{os.getpid()}开始打印!')
        time.sleep(random.randint(1,3))
        print(f'task1-{os.getpid()}打印结束!')
        lock.release()		# 解锁释放
    
    def task2(lock):
    
        lock.acquire()
        print(f'task2-{os.getpid()}开始打印!')
        time.sleep(random.randint(1,3))
        print(f'task2-{os.getpid()}打印结束!')
        lock.release()
    
    def task3(lock):
    
        lock.acquire()
        print(f'task3-{os.getpid()}开始打印!')
        time.sleep(random.randint(1,3))
        print(f'task3-{os.getpid()}打印结束!')
        lock.release()
    
    if __name__ == '__main__':
        mutex = Lock()
        for i in range(1,4):
            p = Process(target=getattr(sys.modules[__name__], f'task{i}'), args=(mutex,))
            p.start()
    
    # 优化,多个用户打印
    
    from multiprocessing import Process
    from multiprocessing import Lock
    import os
    import time
    import random
    
    def task(lock, i):
    
        lock.acquire()
        print(f'用户{i}:{os.getpid()}开始打印!')
        time.sleep(random.randint(1,3))
        print(f'用户{i}:{os.getpid()}打印结束!')
        lock.release()
    
    if __name__ == '__main__':
        mutex = Lock()
        for i in range(1,5):
            p = Process(target=task, args=(mutex, i))
            p.start()
    

    Lock与join对比:

    ​ 相同点:都可以把并发变成串行,保证了顺序。

    ​ 不同点:join是人为设定的顺序;Lock是让其竞争顺序,保证公平性。

    2. 进程之间的通信

    ​ 进程在内存级别是隔离的,但是文件在磁盘上是共享的。

    2.1 基于文件的通信

    当多个进程共同争抢一个数据、资源时,如果要保证顺序、数据的安全,必须要串行。

    缺点:效率低;需人为加锁容易出现死锁。

    # 模拟抢票系统,5个用户抢1张票。(查票时是并发的,但购票时是串行的)
    # 文件ticket_json 中写入{"count":1}
    
    from multiprocessing import Process
    from multiprocessing import Lock
    import time
    import os
    import random
    import json
    
    def search():		# 查看余票
        time.sleep(random.random())
        with open('ticket_json','r', encoding='utf-8') as f1:
            dic = json.load(f1)
            print(f'{os.getpid()}查看余票:{dic["count"]}')
    
    def paid():			# 购票
        with open('ticket_json','r', encoding='utf-8') as f1:
            dic = json.load(f1)
        if dic["count"] > 0:
            dic["count"] -= 1
            time.sleep(random.randint(1,2))
            with open('ticket_json','w', encoding='utf-8') as f2:
                json.dump(dic,f2)
            print(f'{os.getpid()}购票成功!')
        else:
            print(f"{os.getpid()}:已没票!")
    
    def task(lock):		# 子进程
        search()
        lock.acquire()	#购票加锁
        paid()
        lock.release()
    
    if __name__ == '__main__':
        mutex = Lock()
        for i in range(6):		# 5个用户抢1张票
            p = Process(target=task, args=(mutex,))
            p.start()
    

    2.2 基于队列的通信

    队列:存在于内存,可以理解是一个容器。可以承载一些数据。

    特性:先进先出,FIFO。效率较快。

    from multiprocessing import Queue
    
    def func():
        print('is func')
    class Q:
        pass
    obj = Q()
    
    q = Queue(4)	# 最大承载4个数据
    q.put(1)		#添加数据到队列中
    q.put([2])
    q.put(func)
    q.put(obj)
    #q.put(111)  	# 超出会阻塞
    
    for i in range(5):
        print(q.get())  # 依次取出数据,当没数据时,再get会阻塞
    
    
    # 队列Queue中的一些方法、参数
    q = Queue(n) 	# maxsize = n 最大承载n个数据
    q.qsize()		# 获取队列的元素个数
    q.empty()		# 判断队列是否为空
    q.full()		# 判断队列是否已满
    put(self, obj, block=True, timeout=None) 
    get(self, block=True, timeout=None)
    # 队列满时,再put会阻塞,直到某个进程get()数据时,会添加进去。
    # 队列没数据时,再get会阻塞,直到某个进程put()数据时,会取出。
    
    block = True : 默认阻塞。当写block=False时,只有遇到阻塞就会报错。
    q.put(11,block=False)  	# 当队列满时,会报错
    q.get(block=False)  	# 当队列满无数据时时,会报错
    
    timeout = 3  # 阻塞3秒,3秒后还是阻塞状态就会报错。
    
    # 用队列购票
    
    from multiprocessing import Process
    from multiprocessing import Queue
    import os
    import time
    import random
    
    def search(q):  # 查看余票
        print(f"用户-{os.getpid()}查看余票:{q.qsize()}票")
    
    def paid(q):    # 购票
        if q.qsize() > 0:
            q.get()
            print(f"用户-{os.getpid()},购票成功")
        else:
            print(f"用户-{os.getpid()},购票失败")
    
    def task(q):
        search(q)
        time.sleep(random.random())     # 网络延迟
        paid(q)
    
    if __name__ == '__main__':
        q = Queue(10)
        for i in range(3):      # 3张票
            q.put(1)
        for i in range(5):      # 5个用户
            p = Process(target=task, args=(q,))
            p.start()
    

    3. 生产者消费者模型

    模型三要素:

    1. 生产者:产生数据
    
    1. 消费者:接收数据做进一步的处理
    2. 队列:缓冲作用,平衡生产力、消费力,解耦
    from multiprocessing import Process
    from multiprocessing import Queue
    import time
    import random
    
    def priducer(q,name):
    
        for i in range(1,6):
            time.sleep(random.random())
            q.put(f"第{i}个产品")
            print(f"{name}生产了第{i}个产品!")
    
    def consumer(q,name):
    
        while 1:
            try:
                f = q.get(timeout=2)
                time.sleep(random.randint(1,2))
                print(f"33[34;0m{name}使用了{f}33[0m")
            except Exception:
                return
    
    if __name__ == '__main__':
        q = Queue()
        p1 = Process(target=priducer, args=(q, 'meet'))
        p2 = Process(target=consumer, args=(q, 'alex'))
        p1.start()
        p2.start()
    
  • 相关阅读:
    Maven打jar包
    windows关闭占用某端口的进程
    windows系统下发布python模块到pypi
    【转】vmware 安装 osx 无法登录 appstore 的解决办法 (伪造smbios设备信息)
    【转】Java并发编程:volatile关键字解析
    自定义JSP标签
    不一样的ssm
    eclipse制作exe文件
    ftp服务器搭建及简单操作
    OC中的socket通信
  • 原文地址:https://www.cnblogs.com/liwenhu/p/11401665.html
Copyright © 2020-2023  润新知