• 进程,互斥锁,生产者消费者,线程


    进程,互斥锁,生产者消费者,线程

    一、僵尸进程与孤儿进程

    • 代码演示
    '''
    僵尸进程(有坏处):
        - 在子进程结束后,主进程没有正常结束,子进程的PID不会被回收。
    
        缺点:
            - 操作系统中PID号是有限的,比如子进程PID号无法正常回收,则会占用PID号。
            - 资源浪费
            - 若PID号满了,则无法创建新的进程。
    
    孤儿进程(没有坏处):
    
        - 在子进程没有结束时,主进程没有“正常结束”,子进程PID不会被回收。
        - 操作系统优化机制(孤儿院):
            当主进程意外终止,操作系统会检测是否有正在运行的子进程,会将他们放入孤儿院,让操作系统帮你自动回收。
    
    '''
    #孤儿院进程
    from multiprocessing import Process
    from multiprocessing import current_process
    #在子进程中调用,可以拿到子进程对象.pid可以获取pid号
    #在主进程中调用,可以拿到主进程对象.pid可以获取pid号
    import os
    import time
    
    
    def task():
        print(f'start...{current_process().pid}')
        time.sleep(1000)
        print(f'end...{os.getpid()}')
        print('子进程结束啦啊...')
    
    if __name__ == '__main__':
        p = Process(target=task)
    
        p.start()
    
        print(f'进入主进程的io--->{current_process().pid}')
        time.sleep(4)
        print(f'进入主进程的io--->{os.getpid()}')
    
        #主进程结束
        print('主进程结束...')
        print(f'查看主进程{os.getpid()}')
        f = open('yafenghandsome.txt')#此时主进程没有正常结束
    
    
    #僵尸进程
    from multiprocessing import Process
    from multiprocessing import current_process
    #在子进程中调用,可以拿到子进程对象.pid可以获取pid号
    #在主进程中调用,可以拿到主进程对象.pid可以获取pid号
    import os
    import time
    def task():
        print(f'start...{current_process().pid}')
        time.sleep(1)
        print(f'end...{os.getpid()}')
        print('子进程结束啦啦...~~~')
    
    if __name__ == '__main__':
        p = Process(target=task)
    
        p.start()
    
        print(f'进入主进程的io--->{current_process().pid}')
        time.sleep(5)
        print(f'进入主进程的io--->{os.getpid()}')
    
        print('主进程结束...')
        print(f'查看主主进程{os.getppid()}')
        f = open('yafeng6666.txt')
    

    二、子进程回收的两种方式

    • 代码演示
    from multiprocessing import Process
    import time
    
    # def task():
    #     print('start...')
    #     time.sleep(2)
    #     print('end...')
    #
    #
    #
    # #方式一join让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源。
    # if __name__ == '__main__':
    #     p = Process(target=task)
    #
    #     #告诉操作系统帮你开启子进程
    #     p.start()
    #     p.join()
    #
    #     time.sleep(3)
    #
    #     #主进程结束
    #     print('主进程结束...')
    
    
    # 方式二主进程 “正常结束” ,子进程与主进程一并被回收资源。
    
    def task():
        print('start...')
        time.sleep(2)
        print('end...')
    
    
    if __name__ == '__main__':
        p = Process(target=task)
        p.start()
    
        time.sleep(1)
        print('主进程结束...')
    
    
    

    三、进程守护

    • 演示
    '''
           守护进程:
                当主进程结束时,子进程也必须结束,并回收。
    '''
    
    from multiprocessing import Process
    
    import time
    
    
    def demo(name):
        print(f'start....{name}')
        time.sleep(100)
        print(f'end...{name}')
        print('子进程结束啦啊....')
    
    
    if __name__ == '__main__':
        p = Process(target=demo, args=('童子军Jason1号',))
    
        # 守护进程必须在p.start()调用之前设置
        p.daemon = True  # 将子进程p设置为守护进程
    
        p.start()
    
        time.sleep(1)
        print('皇帝驾崩啦...')
    
    
    

    四、进程间数据是隔离的

    • 演示
    from multiprocessing import Process
    import time
    '''
    进程间数据是隔离的
    '''
    
    number = 10
    
    def func():
        global number
        print('子进程1号')
        number = 100
    
    
    def func2(number):
        print('子进程2号')
        number += 100
    
    
    if __name__ == '__main__':
        p_obj = Process(target=func)
        p_obj2 = Process(target=func2, args=(number, ))
    
        p_obj.start()
        p_obj2.start()
    
        p_obj.join()
        p_obj2.join()
        time.sleep(1)
    
        print(f'主进程,{number}') #110  ---> 证明进程间数据不是隔离的
                      #10   ---> 证明进程间数据是隔离的
    
    # 子进程1号
    # 子进程2号
    # 主进程,10
    

    五、进程互斥锁

    • 演示
    from multiprocessing import Process
    from multiprocessing import Lock
    import random
    import time
    import json
    
    #抢票例子
    
    #1、查看余票
    def search(name):
        #1、读取data.json文件中的数据
        with open('data.json', 'r', encoding='utf-8') as f:
            data_dic = json.load(f)
            print(f'用户{name}查看余票,余票还剩:{data_dic.get("number")}!')
    
    
    #2、若有余票,购买成功,票数会减少
    def buy(name):
    
        with open('data.json', 'r', encoding='utf-8') as f:
            data_dic = json.load(f)
    
    
        #谁先进入这一步代表最先抢到票
        if data_dic.get('number') > 0:
            data_dic['number'] -= 1
            time.sleep(random.randint(1, 3))
    
            with open('data.json', 'w', encoding='utf-8') as f:
                json.dump(data_dic, f)
    
            print(f'用户{name}, 抢票成功!')
    
        else:
            print(f'用户{name}, 抢票失败!')
    
    
    def run(name, lock):
        #假设1000个用户过来都可以立马查看余票
        search(name)
    
        lock.acquire()  #加锁
        buy(name)
        lock.release()  #释放锁
    
    
    if __name__ == '__main__':
        lock = Lock()
    
        #开启多进程,实现并发
        for line in range(10):
            p_obj = Process(target=run, args=(f'jason{line}', lock))
            p_obj.start()
    
    
    # 用户jason2查看余票,余票还剩:1!
    # 用户jason0查看余票,余票还剩:1!
    # 用户jason3查看余票,余票还剩:1!
    # 用户jason4查看余票,余票还剩:1!
    # 用户jason1查看余票,余票还剩:1!
    # 用户jason5查看余票,余票还剩:1!
    # 用户jason6查看余票,余票还剩:1!
    # 用户jason7查看余票,余票还剩:1!
    # 用户jason8查看余票,余票还剩:1!
    # 用户jason2, 抢票成功!
    # 用户jason0, 抢票失败!
    # 用户jason3, 抢票失败!
    # 用户jason4, 抢票失败!
    # 用户jason1, 抢票失败!
    # 用户jason5, 抢票失败!
    # 用户jason6, 抢票失败!
    # 用户jason7, 抢票失败!
    # 用户jason8, 抢票失败!
    # 用户jason9查看余票,余票还剩:0!
    # 用户jason9, 抢票失败!
    
    

    六、队列

    • 演示
    from multiprocessing import Queue #multiprocessing 提供队列,先进先出
    from multiprocessing import JoinableQueue  #JoinableQueue提供队列,先进先出
    import queue
    
    #第一种
    # q_obj1 = Queue(5)  #此处的5指的是队列中只能存放5份数据
    #
    # #添加数据到队列中
    # q_obj1.put('热巴!')
    # print('添加1个啦')
    # q_obj1.put('胡歌!')
    # print('添加2个啦')
    # q_obj1.put('亚峰!')
    # print('添加3个啦')
    # q_obj1.put('科比!')
    # print('添加4个啦')
    # q_obj1.put('詹姆斯!')
    # print('添加5个啦')
    
    # #注意:put只要队列满了,会进入阻塞状态
    # q_obj1.put('sean')
    # print('我想添加第六个看会不会报错')
    
    #put_nowait:只要队列满了就会报错
    # q_obj1.put_nowait('sean')
    
    
    # 添加1个啦
    # 添加2个啦
    # 添加3个啦
    # 添加4个啦
    # 添加5个啦
    # queue.Full
    
    
    #get:只要队列中有数据,就能获取数据,若没有会进入阻塞状态
    
    
    # print(q_obj1.get())
    # print(q_obj1.get())
    # print(q_obj1.get())
    # print(q_obj1.get())
    # print(q_obj1.get())
    
    
    #get_nowait:若队列中没有数据获取则会报错
    # print(q_obj1.get_nowait())
    
    # 热巴!
    # 胡歌!
    # 亚峰!
    # 科比!
    # 詹姆斯!
    # _queue.Empty
    
    
    # #第二种方式
    # q_obj1 = JoinableQueue(5)  #此处的5指的是队列中只能存放5份数据
    # #添加数据到队列中
    # q_obj1.put('热巴!')
    # print('添加1个啦')
    # q_obj1.put('胡歌!')
    # print('添加2个啦')
    # q_obj1.put('亚峰!')
    # print('添加3个啦')
    # q_obj1.put('科比!')
    # print('添加4个啦')
    # q_obj1.put('詹姆斯!')
    # print('添加5个啦')
    #
    # # #注意:put只要队列满了,会进入阻塞状态
    # # q_obj1.put('sean')
    # # print('我想添加第六个看会不会报错')
    #
    # # put_nowait:只要队列满了就会报错
    # # q_obj1.put_nowait('sean')
    #
    #
    # # 添加1个啦
    # # 添加2个啦
    # # 添加3个啦
    # # 添加4个啦
    # # 添加5个啦
    # # queue.Full
    #
    #
    # # get:只要队列中有数据,就能获取数据,若没有会进入阻塞状态
    #
    #
    # print(q_obj1.get())
    # print(q_obj1.get())
    # print(q_obj1.get())
    # print(q_obj1.get())
    # print(q_obj1.get())
    #
    #
    # # get_nowait:若队列中没有数据获取则会报错
    # # print(q_obj1.get_nowait())
    # #
    # # 热巴!
    # # 胡歌!
    # # 亚峰!
    # # 科比!
    # # 詹姆斯!
    # # _queue.Empty
    
    
    # 第三种方式
    q_obj1 = queue.Queue(5)  #此处的5指的是队列中只能存放5份数据
    
    #添加数据到队列中
    q_obj1.put('热巴!')
    print('添加1个啦')
    q_obj1.put('胡歌!')
    print('添加2个啦')
    q_obj1.put('亚峰!')
    print('添加3个啦')
    q_obj1.put('科比!')
    print('添加4个啦')
    q_obj1.put('詹姆斯!')
    print('添加5个啦')
    
    #注意:put只要队列满了,会进入阻塞状态
    #q_obj1.put('sean')
    #print('我想添加第六个看会不会报错')
    
    # put_nowait:只要队列满了就会报错
    #q_obj1.put_nowait('sean')
    
    
    # 添加1个啦
    # 添加2个啦
    # 添加3个啦
    # 添加4个啦
    # 添加5个啦
    # queue.Full
    
    
    #get:只要队列中有数据,就能获取数据,若没有会进入阻塞状态
    
    
    print(q_obj1.get())
    print(q_obj1.get())
    print(q_obj1.get())
    print(q_obj1.get())
    print(q_obj1.get())
    
    
    #get_nowait:若队列中没有数据获取则会报错
    #print(q_obj1.get_nowait())
    
    # 热巴!
    # 胡歌!
    # 亚峰!
    # 科比!
    # 詹姆斯!
    # _queue.Empty
    
    

    七、IPC(进程间通信)

    • 演示
    from multiprocessing import Process
    from multiprocessing import JoinableQueue
    import time
    
    '''
    通过队列可实现进程间通信
    '''
    
    def task1(q):
        x = 100
        q.put(x)
        print('添加数据')
    
        time.sleep(3)
        print(q.get())
    
    
    def task2(q):
        #想要在task2中获取task1中的x
        res = q.get()
        print(f'获取的数据{res}')
        q.put(9527)
    
    
    if __name__ == '__main__':
        q = JoinableQueue(10)
    
        #产生两个不同的子进程
        p1 = Process(target=task1, args=(q, ))
        p2 = Process(target=task2, args=(q, ))
    
        p1.start()
        p2.start()
    
    
    # 添加数据
    # 获取的数据100
    # 9527
    

    八、生产者与消费者

    • 演示
    from multiprocessing import JoinableQueue
    from multiprocessing import Process
    import time
    
    #生产者:生产数据---> 队列
    def producer(name, food, q):
        msg = f'{name}生产了{food}食物'
    
        #生产一个食物添加到牌队列中去
        q.put(food)
        print(msg)
    
    
    #消费者: 使用数据 <--- 队列
    def customer(name, q):
        while True:
            try:
                time.sleep(0.5)
    
                #若报错,则跳出循环
                food = q.get_nowait()
                msg = f'{name}吃了{food}食物'
                print(msg)
    
            except Exception:
                break
    
    if __name__ == '__main__':
        q = JoinableQueue()
    
        #创建10个生产者
        for line in range(10):
            p1 = Process(target=producer, args=('yafeng', f'高级食物{line}', q))
            p1.start()
    
    
        #创建两个消费者
        c1 = Process(target=customer, args=('jason', q))
        c2 = Process(target=customer, args=('sean', q))
        c1.start()
        c2.start()
    
    # yafeng生产了高级食物1食物
    # yafeng生产了高级食物0食物
    # yafeng生产了高级食物2食物
    # yafeng生产了高级食物3食物
    # yafeng生产了高级食物4食物
    # yafeng生产了高级食物5食物
    # yafeng生产了高级食物6食物
    # yafeng生产了高级食物8食物
    # yafeng生产了高级食物7食物
    # yafeng生产了高级食物9食物
    # jason吃了高级食物1食物
    # sean吃了高级食物0食物
    # jason吃了高级食物2食物
    # sean吃了高级食物3食物
    # jason吃了高级食物4食物
    # sean吃了高级食物5食物
    # jason吃了高级食物6食物
    # sean吃了高级食物8食物
    # jason吃了高级食物7食物
    # sean吃了高级食物9食物
    

    九、线程以及守护线程

    • 开启线程的方式以及理论知识
    '''
    线程:
        1、什么是线程?
            进程:资源单位
            线程:执行单位
    
            线程与进程都是虚拟的概念,为了更好表达某种食物
    
            注意:开启一个进程,一定会自带一个线程,线程才是真正的执行者。
    
        2、为什么要使用线程?
            节省资源的占用
    
            - 开启进程:
                -1)会产生一个内存空间,申请一块子资源
                -2) 会自带一个主线程
                -3)开启子进程的速度要比开启子线程的速度慢
    
            - 开启线程
                -1);一个进程内可以开启多个线程,从进程的内存空间中申请执行单位
                -2):节省资源
    
            - 开启三个线程
                - 从一个内存资源中,申请三个小的执行单位
    
            - IO密集型用:多线程
                - IO(时间由用户定):
                    - 阻塞:切换 + 保存状态
    
            - 计算密集型用:多进程
                - 计算(时间由操作系统定):
                    - 计算时间很长 ---> 切换 + 保存状态
    
        注意:进程与进程之间数据是隔离的,线程与线程之间数据是共享的
    
        3、怎么使用线程?
        from threading import Thread
    
    '''
    from threading import Thread
    import time
    
    number = 1000
    
    
    # #启动线程的方式一:
    # def task():
    #     global number
    #     number = 100
    #     print('start...')
    #     time.sleep(1)
    #     print('end...')
    #
    #
    # if __name__ == '__main__':
    #     #开启一个子线程
    #     t = Thread(target=task)
    #     t.start()
    #
    #     t.join()
    #     print('主进程(主线程)...')
    #     print(number)
    #
    # # 主进程(主线程)...
    # # 100
    # # start...
    # # end...
    
    
    # #开启线程的方式二:
    # class MyThread(Thread):
    #     def run(self):
    #         print('start...')
    #         time.sleep(1)
    #         print('end...')
    #
    #
    # if __name__ == '__main__':
    #     #开启一个子线程
    #     t = MyThread()
    #     t.start()
    #     # t.join()
    #     print('主进程(主线程)...')
    
    
    • 子线程守护
    from threading import current_thread
    
    number = 1000
    
    def task():
        global number
        number = 100
        print(f'start....{current_thread().name}')
        time.sleep(3)
        print(f'end....{current_thread().name}')
    
    
    if __name__ == '__main__':
    
        for line in range(10):
            t = Thread(target=task)
            #加上守护线程:主进程结束,代表主线程也结束,子线程可能未被回收
            t.daemon = True
            t.start()
    
            print(f'主进程(主线程)....{current_thread().name}')
            print(number)
    
    

    十、线程互斥锁

    • 演示
    from threading import Lock
    from threading import Thread
    import time
    
    
    #开启10个线程,对一个数据进行修改
    number = 100
    
    
    def task():
        global number
    
        lock.acquire()
        number2 = number
        time.sleep(1)
        number = number2 - 1
        lock.release()
    
    
    
    if __name__ == '__main__':
        lock = Lock()
        list1 = []
        for line in range(10):
            t = Thread(target=task)
            t.start()
            list1.append(t)
    
        for t in list1:
            t.join()
    
        print(number)
    
    #>>>> 90
    
    
    
  • 相关阅读:
    (6)在树莓派上截屏的方法
    (7)树莓派读物USB摄像头
    (4)给树莓派安装中文输入法Fcitx及Google拼音输入法
    (3)使用Android手机作为树莓派的屏幕
    (2)在树莓派安装运行在Python3上的OpenCV
    相机靶面尺寸和视场角换算
    STM32F103C8T6在Arduino IDE里编程
    项目(二) esp32-cam 网页图像人脸
    开发(一) ardunio环境配置 针对esp32-cam 更多例程
    [转] Compile、Make和Build的区别
  • 原文地址:https://www.cnblogs.com/yafeng666/p/12003639.html
Copyright © 2020-2023  润新知