python多进程基础详解

编辑: admin 分类: python 发布时间: 2021-12-03 来源:互联网
目录
  • 进程
  • 开启一个进程
  • JOIN方法
  • 进程之间空间隔离
  • 进程的常用方法
    • current_process 查看pid(进程id)
    • os.getpid() 查看进程id
    • 进程其他方法和属性
  • 守护进程
    • 互斥锁
      • 进程间通信(IPC机制)
        • JoinableQueue 来实现生产消费者
          • 总结

            进程

            什么是进程
            进程指的是一个程序的运行过程,或者说一个正在执行的程序
            所以说进程一种虚拟的概念,该虚拟概念起源操作系统

            一个CPU 同一时刻只能执行一件事

            开启一个进程

            from  multiprocessing import Process
            import time
            def task(name):
                print('%s is running'%name)
                time.sleep(3)
                print('%s is done'%name)
            # 开启子进程的操作必须放到
            # if __name__ == '__main__'的子代码中
            # 子进程不会再次加载
            if __name__ == '__main__':
                p=Process(target=task,args=('小王',))
                # p=Process(target=task,kwargs={'name':'小王'})
                # print(p)
                p.start()
                # 主进程只是向操作系统发送了一个开启子进程的信号
                # p.start()
                # 1.操作系统先申请内存空间
                # 2.把主进程的数据拷贝到子进程里面
                # 3.调用cup才能运行里面的代码
                # 创造进程的开销大
                print('主')
            

            在这里插入图片描述

            JOIN方法

            当前进程jion别的进程。当前进程就会等到别的进程执行完毕了才会继续开始往下执行

            在这里插入图片描述

            from multiprocessing import Process
            import time
            
            def task(name, n):
                print('%s is running' % name)
                time.sleep(n)
                print('%s is done' % name)
            
            if __name__ == '__main__':
                start = time.time()
                p_l = []
                for i in range(1, 4):
                    p = Process(target=task, args=('小王%s' % i, i))
                    p_l.append(p)
                    p.start()
                # 主进程等待子进程
                for p in p_l:
                    p.join()
                print('主', (time.time() - start))
            

            进程之间空间隔离

            from multiprocessing import  Process
            # 这个n是主进程里面的值
            n = 100
            def task():
                global n
                # 改的是子进程里面的全局变量
                # 主进程里面没有改
                n = 0
            if __name__ == '__main__':
                p=Process(target=task)
                p.start()
                p.join()
                print(n)
            

            在这里插入图片描述

            进程的常用方法

            current_process 查看pid(进程id)

            # 1. 进程pid:每一个进程在操作系统内都有一个唯一的id号,称之为pid
            from multiprocessing import Process, current_process
            import time
            
            def task():
                print('%s is running' % current_process().pid)
                time.sleep(3)
                print('%s is done' % current_process().pid)
            
            # 开启子进程的操作必须放到
            # if __name__ == '__main__'的子代码中
            # 子进程不会再次加载
            if __name__ == '__main__':
                p = Process(target=task)
                p.start()
                print('主', current_process().pid)
            

            在这里插入图片描述

            os.getpid() 查看进程id

            # os模块也可以
            from multiprocessing import Process, current_process
            import time, os
            
            def task():
                print('%s is running 爹是%s' % (os.getpid(), os.getppid()))
                time.sleep(3)
                print('%s is done爹是%s' % (os.getpid(), os.getppid()))
            
            # 开启子进程的操作必须放到
            # if __name__ == '__main__'的子代码中
            # 子进程不会再次加载
            if __name__ == '__main__':
                p = Process(target=task)
                p.start()
                # 谁把主进程创造出来的
                #   用pycharm就是pycharm创造的
                print('主%s爹是%s' % (os.getpid(), os.getppid()))
            
            

            在这里插入图片描述

            进程其他方法和属性

            from multiprocessing import Process,current_process
            import time,os
            def task():
                print('%s is running 爹是%s'%(os.getpid(),os.getppid()))
                time.sleep(30)
                print('%s is done爹是%s'%(os.getpid(),os.getppid()))
            # 开启子进程的操作必须放到
            # if __name__ == '__main__'的子代码中
            # 子进程不会再次加载
            if __name__ == '__main__':
                p=Process(target=task)
                p.start()
                # 谁把主进程创造出来的
                # 用pycharm就是pycharm创造的
                # 进程的名字
                print(p.name)
                # 杀死子进程
                p.terminate()
                # 需要时间
                time.sleep(0.1)
                #  判断子进程是否存活
                print(p.is_alive())
                print('主%s爹是%s'%(os.getpid(),os.getppid()))
            

            在这里插入图片描述

            守护进程

            本质就是一个"子进程",该"子进程"的生命周期<=被守护进程的生命周期
            当被守护的进程执行完了。它也会被杀死

            # 主进程运行完了,子进程没有存在的意义
            # 皇帝和太监不是同生,但是是同死
            from  multiprocessing import Process
            import time
            def task(name):
                print('%s活着'%name)
                time.sleep(3)
                print('%s正常死亡'%name)
            if __name__ == '__main__':
                p1=Process(target=task,args=('老太监',))
                # 声明子进程为守护进程
                p1.daemon = True
                p1.start()
                time.sleep(1)
                print('皇帝正在死亡')
            

            在这里插入图片描述

            互斥锁

            进程之间内存空间互相隔离,怎样实现共享数据
            进程之间内存数据不共享,但是共享同一套文件系统,所以访问同一个文件,是没有问题的,
            而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理

            '''
            抢票
                查票
                购票
            互斥锁:
                在程序中进行加锁处理
                必须要释放锁下一个锁才能获取,所以程序在合适的时候必须要有释放锁
            所以用文件来处理共享数据
                1.速度慢
                2.必须有互斥锁
            '''
            import json
            import time,random
            from multiprocessing import Process,Lock
            # 查票
            def search(name):
                with open('db.json','rt',encoding='utf-8')as f:
                    dic = json.load(f)
                # 模拟查票时间
                time.sleep(1)
                print('%s 查看到余票为 %s'%(name,dic['count']))
            # 购票
            # 第二个get子进程不会是第一个get子进程修改后count的结果
            # 加互斥锁,把这一部分并发变成串行,
            # 但是牺牲了效率,保证了数据安全
            def get(name):
                with open('db.json','rt',encoding='utf-8')as f:
                    dic = json.load(f)
                if dic['count']>0:
                    dic['count']-=1
                    time.sleep(random.randint(1,3))
                    with open('db.json', 'wt', encoding='utf-8')as f:
                        json.dump(dic,f)
                        print('%s 购票成功'%name)
                else:
                    print('%s 查看到没有票了'%name)
            def task(name,mutex):
                # 并发
                search(name)
                # 串行
                # 加互斥锁
                mutex.acquire()
                get(name)
                # 释放互斥锁
                mutex.release()
            # if __name__ == '__main__':
            #     for i in range(10):
            #         p=Process(target=task,args=('路人%s'%i,))
            #         p.start()
            #         #  join只能将进程的任务整体变成串行
            #         # 互斥锁可以局部串行
            #         p.join()
            #         # 数据安全,是指读的时候无所谓,写的(改的)时候必须安全
            #         # 写的时候是串行,读的时候并发
            # 加锁
            if __name__ == '__main__':
                # 主进程加锁
                mutex=Lock()
                for i in range(10):
                    # 锁传入子进程
                    p=Process(target=task,args=('路人%s'%i,mutex))
                    p.start()
                    #  join只能将进程的任务整体变成串行
                    # 互斥锁可以局部串行
                    # p.join()
                    # 数据安全,是指读的时候无所谓,写的(改的)时候必须安全
                    # 写的时候是串行,读的时候并发
            

            db.json 中只有10张票。如果没有加锁。则可能会出现票呗多卖的情况
            在这里插入图片描述

            进程间通信(IPC机制)

            '''
            速度快
            锁问题解决
            ipc机制
                进程彼此之间互相隔离,要实现进程间通信(IPC),
                multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的
                共享内存空间
                队列=管道+锁
            '''
            from  multiprocessing import Queue
            # 占用的内存,最好小数据,消息数据,下载地址
            # Queue(限制队列里面的个数)
            # 先进先出
            q=Queue(3)
            # 添加
            q.put('a')
            q.put('b')
            q.put({'x':2})
            print('篮子满了')
            # 队列满了,相当于锁了
            # q.put({'x':2})
            # 提取
            print(q.get())
            print(q.get())
            print(q.get())
            # # 队列为空,等待加入,也会阻塞,相当于锁了
            print('队列为空')
            print(q.get())
            

            队列被取完了 后面的q.get() 会阻塞直到有新的元素。所以程序不会结束

            在这里插入图片描述

            JoinableQueue 来实现生产消费者

            JoinableQueue#task_done()方法当队列里面没有元素会结束线程

            '''
            小王和小周每人生产10分包子和土豆丝
            小戴和小杨一直吃,当队列里面没有食物时。终结进程
            '''
            import time, random
            from multiprocessing import Process, JoinableQueue
            
            def producer(name, food, q):
                for i in range(10):
                    res = '%s%s' % (food, i)
                    # 模拟生产数据的时间
                    time.sleep(random.randint(1, 3))
                    q.put(res)
                    print('厨师%s生成了%s' % (name, res))
            
            def consumer(name, q):
                while True:
                    # 订单都没了还在等,队列里面空了
                    res = q.get()
                    # 模拟处理数据的时间
                    time.sleep(random.randint(1, 3))
                    print('吃货%s吃了%s' % (name, res))
                    # 1每次完成队列取一次,往q.join() ,取干净了q.join()运行完
                    q.task_done()
            
            # 多个生产者和消费者
            if __name__ == '__main__':
                q = JoinableQueue()
                # 生产者
                p1 = Process(target=producer, args=('小王', '包子', q))
                p3 = Process(target=producer, args=('小周', '土豆丝', q))
                # 消费者
                c1 = Process(target=consumer, args=('小戴', q))
                c2 = Process(target=consumer, args=('小杨', q))
                # #3.守护进程的作用: 主进程死了,消费者子进程也跟着死
                #     #把消费者变成守护进程
                c1.daemon = True
                c2.daemon = True
                p1.start()
                p3.start()
                c1.start()
                c2.start()
                p1.join()
                p3.join()
                # 2消费者task_done给q.join()发信号
                q.join()
                print('主')
                # 生产者运行完?1,2
                # 消费者运行完?1,2
            

            当队列为空时,不会傻傻等待而是结束进程

            在这里插入图片描述

            总结

            本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注hwidc的更多内容!

            【来源:海外服务器https://www.68idc.cn】