zoukankan      html  css  js  c++  java
  • 第五十三篇 并发编程之多进程续

    第五十三篇 并发编程之多进程续

    一、守护进程

    1.守护进程的特性

    1.守护进程会在主进程代码执行结束后就终止(而守护进程结束不会影响主进程的执行)

    2.守护进程内无法再启动子进程,否则会抛出异常:AssertionError: daemonic processes are not allowed to have children

    from multiprocessing import Process
    import time
    import random
    
    class MyProcess(Process):
    	def __init__(self, name):
    		self.name = name
    		super().__init__()
    		
    	def run(self):
    		print(self.name,'start')
    		time.sleep(random.randrange(1,4))
    		print(self.name,'end')
    		
    if __name__ == '__main__':
    	p = MyProcess('king')
    	p.daemon = True
    	print('主进程  start')
    	p.start()
    	time.sleep(3)
    	print('主进程  end')
    

    二、进程安全问题

    1.进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的

    2.但是由于共享会带来资源的竞争,竞争的结果就是错乱,所以就出现了进程安全的问题,也就是合理有序的共享有限资源的问题

    3.解决方法:

    • 1.直接使用join函数:所有代码全都串行(还不如不开进程),多个进程之间原本公平进制,join则会强行规定执行顺序

    • 2.互斥锁:原理就是将要操作公共资源的代码锁起来,以保证同一时间只能有一个进程在执行这部分代码

    三、互斥锁

    1.同步的概念

    同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。"同"字从字面上容易理解为一起动作,其实不是,"同"字应是指协同、协助、互相配合。如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B执行,再将结果给A;A再继续操作

    2.锁的原理

    **1.将要操作公共资源的代码锁起来, 以保证同一时间只能有一个进程在执行这部分代码 **

    2.互相排斥的锁

    3.优点:可以仅将部分代码串行

    4.注意:必须保证锁只有一把

    3.锁的应用

    1.加锁解决了安全问题,带来了效率减低的问题
    2.锁其实只是给执行代码加了限制,本质是一个标志(True或False)
    3.如何使得即保证安全又提高效率:

    • 锁的粒度(粒度指的是被锁住的代码的多少),粒度越大锁住的就越多,效率也就越低
    from multiprocessing import Process, Lock
    import random
    import time
    
    
    def task1(mutex):
        for i in range(10000):
            print(1)
    
        mutex.acquire()
        time.sleep(random.random())  # 假如随机睡眠,是为了表现两个进程可能同时运行的情况
        print('...task1 start...')
        time.sleep(random.random())
        print('...task1 continue...')
        time.sleep(random.random())
        print('...task1 end...')
        mutex.release()
    
    def task2(mutex):
        for i in range(10000):
            print(2)
    
        mutex.acquire()
        time.sleep(random.random())
        print('*** task2 start...')
        time.sleep(random.random())
        print('*** task2 continue...')
        time.sleep(random.random())
        print('*** task2 end...')
        mutex.release()
    
    if __name__ == '__main__':
        mutex = Lock()
        p1 = Process(target=task1, args=(mutex,))
        p2 = Process(target=task2, args=(mutex,))
    
        p1.start()
        p2.start()
    

    5.抢票

    def show():
        with open('db.json', 'r') as fr:
            data = json.laod(fr)
            print(data['count'])
            
    def buy():
        with open('db.json', 'r') as fr:
            data = json.load(fr)
            if data['count'] > 0:
                data['count'] -= 1
                with open('db.json', 'w') as fw:
                    json.dump(data, fw)
                    print('ok')
                    
    def task(mutex):
        show()
        mutex.acquire()
        buy()
        mutex.release()
        
    if __name__ == '__main__':
    	mutex = Lock()
        for i in range(2):
        	p = Process(target=task, args=(mutex,))
            p.start()
    

    6.总结

    • 1. 通过multiprocessing导入Lock类
    • 2.进程调用锁的acquire()方法,锁就会进入“locked”状态
    • 3.调用release()方法,锁进入“unlocked”状态

    IPC

    manager和队列都是基于共享内存而实现的

    四、Manager的使用

    作用:创建进程间同步的容器
    缺点:没有处理安全问题,并不常用

    from multiprocessing import Process,Manager,Lock
    import time
    
    def task(data,lock):
        lock.acquire()
        num = data[0]
        time.sleep(0.2)
        data[0] = num - 1   # data改变,syncdict的值也会改变
        lock.release()
    
        print("子 over")
    
    
    if __name__ == '__main__':
    
        d = [100]
        m = Manager() # 创建一个管理器
        syncdict = m.dict(d) # 让管理器创建一个进程同步的字典
        # syncdict = m.list(d)    # sync 同步
    
        # 创建一个锁
        lock = Lock()
    
        ps = []
        for i in range(10):
            # 
            p = Process(target=task,args=(syncdict,lock))
            p.start()
            ps.append(p)
    
    
        for p in ps: p.join()
    
        print(d)   # 不变
        print(syncdict)  # 改变,和子进程中的同步
    

    五、进程队列

    1.Queue 翻译为队列 是一种特殊的容器
    2.特殊之处在于存取顺序为先进先出
    3.作用:可以帮我们完成进程间通讯

    from multiprocessing import Queue
    
    q = Queue(2) # 创建队列 并且同时只能存储2个元素
    q.put(1)
    q.put(2)
    
    # q.put(3,block=True,timeout=3) # 默认是阻塞的 当容器中没有位置了就阻塞 直到有人从里面取走元素为止
    print(q.get())
    print(q.get())
    print(q.get(block=True,timeout=3))# 默认是阻塞的 当容器中没有位置了就阻塞 直到有人存入元素为止
    # block有方块的意思,也有阻塞的意思
    

    六、函数调用栈

    1.也是一种特殊的容器
    2.存取顺序为先进后出
    3.调用函数时,称之为函数入栈(也叫压栈)
    4.函数执行结束时,称之为函数出栈

    七、处理万能异常

    import traceback
    
    try:
    	raise Exception('1/0')
    except Exception as e:
    	traceback.print_exc()   # 可以追踪到具体的出错代码
    	print(e)
    print('可以执行...')   # 捕捉了异常之后,后面的代码也可以正常执行
    

    八、生产者消费模型

    模型就是套路、模板(解决某种固定问题的固定套路)

  • 相关阅读:
    [杂说]网络是基础生产工具
    这几天的工作
    [代码]大家来动动脑筋吧
    测试
    [基础] 如何使用extern和static限定符
    元宵节快乐
    复杂的“人"
    C# SMTP发邮件不支持465端口的解决方案,网易企业邮箱
    软件三层架构模型
    ASP.NET MVC 使用二级域名来注册Area区域
  • 原文地址:https://www.cnblogs.com/itboy-newking/p/11185343.html
Copyright © 2011-2022 走看看