• 并发编程之进程基础


    一、join() 方法

    阻塞当前进程,直到调用join方法的那个进程执行完,再继续执行当前进程

    控制进程是否结束,进程结束就结束阻塞

    使进程处于阻塞状态,直到进程执行完就结束阻塞

    相当于同步控制

    from multiprocessing import Process
    
    def sendmail(argv):
        print('快递%d已送达'%argv)
    
    if __name__ == '__main__':
        li = []
        for i in range(10):
            p = Process(target=sendmail,args=(i,))      # 创建子进程对象
            p.start()                                   # 启动子进程
            li.append(p)                                # 将对象假如到列表
        for p in li:                                    # 循环对象的列表
            p.join()                                    # 使进程处于阻塞状态,直到进程执行完就结束阻塞
        print('10件快件已发送完毕')
        
    '''
    结果
    快递1已送达
    快递0已送达
    快递2已送达
    快递4已送达
    快递3已送达
    快递5已送达
    快递6已送达
    快递8已送达
    快递7已送达
    快递9已送达
    10件快件已发送完毕
    '''

     

    二、第二种开启子进程的方式

    面向对象的方式启动,创建类且继承Process方法,并重写run方法

    start()是开启子进程,让子进程执行run方法

    注意:多个进程不能同时往一个文件写,造成资源抢占,令指针混乱

    from multiprocessing import Process
    import os
    class Myprogress(Process):              # 继承rocess类
        def run(self):                      # 重写run方法
            print('子进程:%d,父进程:%d'%(os.getpid(),os.getppid()))
    
    if __name__ == '__main__':
        for i in range(5):
            p = Myprogress()                # 创建对象
            p.start()                       # 开启进程
        print('主进程:%d'%os.getpid())
        
    '''
    结果
    主进程:484
    子进程:8752,父进程:484
    子进程:11244,父进程:484
    子进程:9804,父进程:484
    子进程:12792,父进程:484
    子进程:6868,父进程:484
    '''

     

    传参数给子进程

    from multiprocessing import Process
    
    class Mycourse(Process):
        def __init__(self,argv):        # 初始化方法
            super().__init__()          # 继承父类的初始化方法
            self.argv = argv            # 创建一个变量,用于传参
    
        def run(self,):                 # 启动进程执行的方法
            print('子进程%d号'% self.argv)
    
    if __name__ == '__main__':
        for i in range(1,10,2):
            p = Mycourse(i)
            p.start()
        print('主进程')
    
    '''
    结果
    主进程
    子进程3号
    子进程1号
    子进程5号
    子进程7号
    子进程9号
    '''

     

    第二种方式开启子进程并加入join方法

    from multiprocessing import Process
    
    class Mycourse(Process):
        def __init__(self,argv):        # 初始化方法
            super().__init__()          # 继承父类的初始化方法
            self.argv = argv            # 创建一个变量,用于传参
    
        def run(self,):                 # 启动进程执行的方法
            print('子进程%d号'% self.argv)
    
    if __name__ == '__main__':
        pro_lis = []
        for i in range(1,10,2):
            p = Mycourse(i)
            p.start()
            pro_lis.append(p)
        for p in pro_lis:
            p.join()                    # 阻塞当前进程,直到调用join方法的那个进程执行完,再继续执行当前进程
        print('主进程')
    
    '''
    结果
    子进程1号
    子进程7号
    子进程3号
    子进程5号
    子进程9号
    主进程
    '''

    三、守护进程

    p.daemon = True 设置p进程为守护进程,必须在start方法之前完成

    守护进程会随着主进程的代码执行完毕而结束

    主进程代码已经执行完,但是子进程还没执行完,守护进程都不会继续执行。

    子进程要在主进程结束前结束

    主进程会等待子进程结束,守护进程之等待主进程代码结束就结束了

    守护进程的作用:程序的报活

    from multiprocessing import Process
    import time
    def func():
        for i in range(1,10):
            print('子进程%d号'% i)
            time.sleep(1)
    
    if __name__ == '__main__':
        p = Process(target=func)
        p.daemon = True             # 设置进程为守护进程
        p.start()
        time.sleep(3)
        print('主进程结束')
        
    '''
    结果
    子进程1号
    子进程2号
    子进程3号
    主进程结束
    '''

    四、其他方法

    p.terminate()#关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活

    p.pid  查看子进程的pid

    from multiprocessing import Process
    import time
    import os
    
    def func():
        for i in range(1,10):
            time.sleep(1)
            print('%d秒' % i,'进程号:',os.getpid())
    
    if __name__ == '__main__':
        p = Process(target=func)
        p.start()
        print(p.pid)            # 查看子进程的pid号
        time.sleep(3)
        p.terminate()           # 关闭子进程进程,但不是马上关闭
        print(p.is_alive())     # 查看子进程的状态
        time.sleep(1)
        print(p.is_alive())     # 查看子进程的状态
    
    '''
    结果
    10720
    1秒 进程号: 10720
    2秒 进程号: 10720
    True
    False
    '''

    五、锁(互斥锁)

    步骤:

    实例化锁对象,把锁对象传给子进程

    锁对象.acquire()  给代码上锁

    锁对象.release()  释放锁

    上锁为了保证数据的安全,在异步的情况下,多个进程有可能同时修改同一份资源,就给修改的过程加上锁

    加锁降低了程序的效率,让原来能够同时执行的代码变成了顺序执行了,异步变同步的过程,保证了数据的安全

    from multiprocessing import Process
    from multiprocessing import Lock
    import json
    
    def get_ticket(name,lock):
        lock.acquire()
        with open('ticket') as f:
            dic = json.load(f)
        if dic['count'] > 0:
            with open('ticket','w') as f1:
                dic['count'] -= 1
                json.dump(dic,f1)
                print('%s已买票'% name)
        else:print('%s走路回家'% name)
        lock.release()
    
    
    if __name__ == '__main__':
        name_lis = ['小明','小红','小蓝','小黑','小白','小青','小小','小树']
        lock = Lock()           # 创建锁对象
        for i in range(8):
            p = Process(target=get_ticket,args=(name_lis[i],lock))
            p.start()
    
    '''
    小明已买票
    小红已买票
    小蓝已买票
    小黑已买票
    小白已买票
    小青走路回家
    小小走路回家
    小树走路回家
    '''

    死锁:在同一个级才能中,连续acquire()两次或以上,程序就不能继续执行,造成了死锁现象

    六、信号量

    锁的变形,多个锁,使部分进程可以执行代码

    Semaphore信号量,保证资源不会被多进程执行

    使用方法跟锁类似

    信号量的实现机制:用计算器+锁实现

    from multiprocessing import Process
    from multiprocessing import Semaphore
    import time
    
    def get_ticket(name,sem):
        sem.acquire()
        print('%s 打饭'% name)
        time.sleep(2)
        print('%s 离开' % name)
        sem.release()
    
    
    if __name__ == '__main__':
        name_lis = ['小明','小红','小蓝','小黑','小白','小青','小小','小树']
        sem = Semaphore(4)           # 创建信号量对象
        for i in range(8):
            p = Process(target=get_ticket,args=(name_lis[i],sem))
            p.start()
            
    '''
    结果
    小明 打饭
    小红 打饭
    小蓝 打饭
    小黑 打饭
    小明 离开
    小青 打饭
    小红 离开
    小白 打饭
    小蓝 离开
    小小 打饭
    小黑 离开
    小树 打饭
    小青 离开
    小白 离开
    小小 离开
    小树 离开
    '''

    七、事件

    Event 模块

    方法: wait()

    wait是否阻塞使看event对象内部的一个属性的值,默认是False

    控制属性的值的方法:

    set()    将属性的值改为True

    clear()    将属性的值改为False

    is_set()   判断当前属性的值是否为True

    例子:红绿灯

    from multiprocessing import Event
    from multiprocessing import Process
    import time
    
    def traffic_light(e):
        while 1:
            if e.is_set():          #
                e.clear()           # 设置状态为False
                print('33[31m红灯亮33[0m')
                time.sleep(3)
    
    
            else:
                print('33[32m绿灯亮33[0m')
                e.set()             # 设置状态为True
                time.sleep(5)
    
    
    def car(e,name):
        e.wait()
        time.sleep(0.5)
        if e.is_set():
            print('%s 通行了' % name)
    
    if __name__ == '__main__':
        cat_list = ['昌河','奇瑞','东风','吉利','长城','一汽']
        e = Event()
        p1 = Process(target=traffic_light,args=(e,))
        p1.daemon = True             # 将红绿灯设置为守护进程
        p1.start()
    
        p2_lis = []
        for i in range(6):
            p2 = Process(target=car,args=(e,cat_list[i]))
            p2.start()
            p2_lis.append(p2)
            time.sleep(2)
            p2.join()               # 设置当前进程阻塞,使子进程执行完才执行当前进程
        print('车辆全部通过')
    
    '''
    绿灯亮
    昌河 通行了
    奇瑞 通行了
    东风 通行了
    红灯亮
    绿灯亮
    吉利 通行了
    长城 通行了
    一汽 通行了
    车辆全部通过
    '''
  • 相关阅读:
    [日常工作]WorkStation 使用端口转发的方式使用宿主机IP地址提供服务
    [日常工作]虚拟机或者实体机转换成HyperV虚拟机的方法
    [linux学习]sysctl 以及 net.ipv4.ip_forward
    [自学]Docker system 命令 查看docker镜像磁盘占用情况 Docker volume 相关
    Docker 修改默认存储路径的一个方法
    [学习笔记]Ubuntu下安装配置SQLSERVER2017
    VSCODE安装以及使用Python运行调试代码的简单记录
    Win2012r2 以及win2016 安装.NET3.5
    Win2016以及win10 IIS10 下安装IEwebcontrol的方法
    [日常工作]协助同事从不能开机的机器上面获取资料信息
  • 原文地址:https://www.cnblogs.com/st-st/p/9674787.html
Copyright © 2020-2023  润新知