• python35 1.守护进程 2.互斥锁3.IPC 4.生产者和消费者模型


    复习
    1.TCP粘包问题
    2.UDP

    3.进程
    1.TCP粘包问题
    2.UDP
    区别:
    UDP 不粘包,不可靠,效率高,适合数据量小的传输,不要求顺序,不需要建立连接
    使用场景:
    UDP 适用于,对速度要求高,但是对数据完整性要求不高,DNS,对战游戏

    网络编程:目前的 无法并发处理多个客户端

    进程
    一个正在运行的程序称之为进程
    进程来自于操作系统
    在第三代操作系统中,诞生了多道技术
    多道技术 是为了提高计算机资源的利用率,第三代以前是串行执行任务的批处理方式
    空间复用
    同一时间在内存中存储多个程序的数据
    内存区域要相互隔离,物理隔离,你是不可能随意访问的
    时间复用
    切换+保存
    当一个进程遇到了IO操作时,就切换其他进程来执行,切换前要记录当前运行状态
    切换条件:
    遇到了IO
    运行时间过长,时间片用完了

    什么时候要开启进程
    当有一个任务,执行时间过长时,为了提高效率,我们就可以将这个任务交给子进程来完成
    linux 与windows开启子进程的区别
    linux 会把数据完整copy给子进程 ,作为子进程的初始状态
    windows 会重新导入父进程的代码来获取需要数据,这样一来创建进程的代码又被执行了一次,造成递归创建进程
    所以要将开启子进程的代码放到 if __name__ == "__main__":中 保证创建进程的代码只被父进程执行
    如何开启子进程
    1.导入 multiprocessing 中的 Process
    实例化Process类 用target来指定要执行任务函数
    调用start来开启进程
    2.创建一个类,继承Process,覆盖run方法,将任务代码放到run中

    进程之间内存相互隔离

    僵尸进程 有害
    子进程结束了,父进程还在运行,子进程会占用pid 并且将保留最后的运行状态在内存中
    在linux中 父进程需要调用wait/waitpid来获取子进程的残留信息,并清理它
    python中已经封装好了 wait 操作不需要我们自己来清理

    如果出现很多僵尸进程
    清除僵尸进程的方法就是 杀死父进程

    孤儿
    父进程已经挂了 ,子进程还在运行,会被移交给操作系统来管理

    常用属性:
    join 父进程等待子进程运行结束 其实是提高子进程的优先级
    is_alive 是否存活
    getpid 获取自己的进程id
    name 进程的名字
    daemon
    terminate 终止这个子进程 有延迟
    start 启动进程 有延迟
    因为开启和关闭进程 都是操作系统来完成

    进程的状态
    运行 -io> 阻塞 -> 就绪态
    运行 -时间片用完了(运行时间过长)> 就绪态

    阻塞
    当程序遇到了IO操作,就进入了阻塞态
    非阻塞
    程序正在运行中,没有任何IO操作
    指的是程序的运行状态

    并行
    多个程序同时运行,是真正的同时执行,仅在多核中会出现

    并发
    多个事件同时发生了,看起来像是都在运行,本质上是切换执行

    程序员永恒的话题
    提高程序的效率 减少IO 力求尽可能多占用CPU
     
     
    今日内容:
    1.守护进程
    2.互斥锁
    3.IPC
    4.生产者和消费者模型
     
    1.守护进程  了解
    什么是守护进程
    ​ 进程是一个正在运行的程序
    ​ 守护进程也是一个普通进程 
    ​ 意思是一个进程可以守护另一个进程
    例如
    ​ 康熙要是一个进程的话,后宫佳丽都是守护者 
    ​ 如果康熙挂了, 后宫佳丽们要陪葬
    结论:
    如果b是a的守护进程,a是被守护的进程,a要是挂了,b也就随之结束了
    测试:
    ```python
    from multiprocessing import Process
    import time
    # 妃子的一生
    def task():
        print("入宫了.....")
        time.sleep(50)
        print("妃子病逝了......")

    if __name__ == '__main__':
        # 康熙登基了
        print("登基了.....")
        # 找了一个妃子
        p = Process(target=task)
        # 设置为守护进程 必须在开启前就设置好
        p.daemon = True
        p.start()
        # 康熙驾崩了
        time.sleep(3)
        print("故事结束了!")
    ```
    使用场景:
    ​ 父进程交给了子进程一个任务,任务还没有完成父进程就结束了,子进程就没有继续执行的意义了
    ​ 例如:qq 接收到一个视频文件,于是开启了一个子进程来下载,如果中途退出了qq,下载任务就没必须要继续运行了
     
    2.互斥锁      重点
        什么是互斥锁
     互斥锁   互相排斥的锁,我在这站着你就别过来,(如果这个资源已经被锁了,其他进程就无法使用了)
    需要强调的是: 锁 并不是真的把资源锁起来了,只是在代码层面限制你的代码不能执行
           为什么需要互斥锁:
    并发将带来资源的竞争问题
    当多个进程同时要操作同一个资源时,将会导致数据错乱的问题
    如下列所示:
            解决方案1:
    ​    加join,
    ​    弊端 1.把原本并发的任务变成了穿行,避免了数据错乱问题,但是效率降低了,这样就没必要开子进程了
    ​         2.原本多个进程之间是公平竞争,join执行的顺序就定死了,这是不合理的
             解决方案2:
    ​    就是给公共资源加锁,互斥锁
    ​    互斥锁   互相排斥的锁,我在这站着你就别过来,(如果这个资源已经被锁了,其他进程就无法使用了)
    锁 并不是真的把资源锁起来了,只是在代码层面限制你的代码不能执行
    #### 锁和join的区别:
    ​ 1.
    ​ join是固定了执行顺序,会造成父进程等待子进程
    ​ 锁依然是公平竞争谁先抢到谁先执行,父进程可以做其他事情
    ​ 2.最主要的区别:
    ​ join是把进程的任务全部串行
    ​ 锁可以锁任意代码 一行也可以  可以自己调整粒度

    ### 案例:

    from multiprocessing import Process,Lock
    import time,random
    def task1(lock):
        # 要开始使用了 上锁
        lock.acquire()       #就等同于一个if判断
        print("hello iam jerry")
        time.sleep(random.randint(0, 2))
        print("gender is boy")
        time.sleep(random.randint(0, 2))
        print("age is  15")
        # 用完了就解锁
        lock.release()
    def task2(lock):
        lock.acquire()
        print("hello iam owen")
        time.sleep(random.randint(0,2))
        print("gender is girl")
        time.sleep(random.randint(0,2))
        print("age is  48")
        lock.release()
    def task3(lock):
        lock.acquire()
        print("hello iam jason")
        time.sleep(random.randint(0,2))
        print("gender is women")
        time.sleep(random.randint(0,2))
        print("age is  26")
        lock.release()
    if __name__ == '__main__':
        lock = Lock()
        p1 = Process(target=task1,args=(lock,))
        p2 = Process(target=task2,args=(lock,))
        p3 = Process(target=task3,args=(lock,))
        p1.start()
        # p1.join()
        p2.start()
        # p2.join()
        p3.start()
        # p3.join()
        # print("故事结束!")
       
    # 锁的伪代码实现
    # if my_lock == False:
    #     my_lock = True
    #   #被锁住的代码
       my_lock = False 解锁
    ```
    注意1:  不要对同一把执行多出acquire 会锁死导致程序无法执行  一次acquire必须对应一次release

     l = Lock()
     l.acquire()
     print("抢到了!")
     l.release()
     l.acquire()
     print("强哥毛线!")
    ```
    注意2:想要保住数据安全,必须保住所有进程使用同一把锁
     
    3.IPC
    ​ 进程间通讯   
    ​ 通讯指的就是交换数据
    ​ 进程之间内存是相互隔离的,当一个进程想要把数据给另外一个进程,就需要考虑IPC
    方式:
    ​ 管道: 只能单向通讯,数据都是二进制 
    ​ 文件: 在硬盘上创建共享文件 
    ​  缺点:速度慢
    ​  优点:数据量几乎没有限制 
    ​ socket:
    ​  编程复杂度较高 
    ​ 共享内存:必须由操作系统来分配    要掌握的方式*****
    ​  优点: 速度快
    ​  缺点: 数据量不能太大
    ## 共享内存的方式
    ##  1.Manager类 了解
    ​  Manager提供很多数据结构  list dict等等
    ​  Manager所创建出来的数据结构,具备进程间共享的特点
    ```python
    from multiprocessing import Process,Manager,Lock
    import time

    def task(data,l):
        l.acquire()
        num = data["num"] #
        time.sleep(0.1)
        data["num"] = num - 1
        l.release()
    if __name__ == '__main__':
        # 让Manager开启一个共享的字典
        m = Manager()
        data = m.dict({"num":10})
        l = Lock()
        for i in range(10):
            p = Process(target=task,args=(data,l))
            p.start()
        time.sleep(2)
        print(data)
    ```
    ​ 需要强调的是 Manager创建的一些数据结构是不带锁的 可能会出现问题
    ## 2.Queue队列   帮我们处理了锁的问题   重点
    ​  队列是一种特殊的数据结构,先存储的先取出    就像排队    先进先出
    ​  相反的是堆栈,先存储的后取出, 就像衣柜 桶装薯片    先进后出
    ​  扩展:
    ​  函数嵌套调用时  执行顺序是先进后出     也称之为函数栈 
    ​  调用 函数时  函数入栈   函数结束就出栈
    ```python
    from multiprocessing import Queue
    # 创建队列  不指定maxsize 则没有数量限制
    q = Queue(3)
    # 存储元素
    # q.put("abc")
    # q.put("hhh")
    # q.put("kkk")
    # print(q.get())
    # q.put("ooo")    # 如果容量已经满了,在调用put时将进入阻塞状态 直到有人从队列中拿走数据有空位置 才会继续执行
    #取出元素
    # print(q.get())# 如果队列已经空了,在调用get时将进入阻塞状态 直到有人从存储了新的数据到队列中 才会继续
    # print(q.get())
    # print(q.get())

    #block 表示是否阻塞 默认是阻塞的   # 当设置为False 并且队列为空时 抛出异常
    q.get(block=True,timeout=2)
    # block 表示是否阻塞 默认是阻塞的   # 当设置为False 并且队列满了时 抛出异常
    # q.put("123",block=False,)
    # timeout 表示阻塞的超时时间 ,超过时间还是没有值或还是没位置则抛出异常  仅在block为True有效
    ```
    4.生产者消费者模型  重点
    ## 是什么
    ​ 模型 就是解决某个问题套路
    ​ 产生数据的一方称之为生产者
    ​ 处理数据的一方称之为消费者
    ​ 例如: 爬虫   生活中到处都是这种模型
    ​  饭店 厨师就是生产者   你吃饭的人就是消费者
    ​ 
    ## 生产者和消费者出啥问题了?   解决什么问题
    ​ 生产者和消费,处理速度不平衡,一方快一方慢,导致一方需要等待另一方
    ## 生产者消费者模型解决这个问题的思路:     怎么解决
    ​ 原本,双方是耦合 在一起,消费必须等待生产者生成完毕在开始处理, 反过来
    ​ 如果消费消费速度太慢,生产者必须等待其处理完毕才能开始生成下一个数据
    ###  解决的方案:
    ​ 将双方分开来.一专门负责生成,一方专门负责处理 
    ​ 这样一来数据就不能直接交互了 双方需要一个共同的容器  
    ​ 生产者完成后放入容器,消费者从容器中取出数据
    ​ 这样就解决了双发能力不平衡的问题,做的快的一方可以继续做,不需要等待另一方
    案例:

    def eat(q):
        for i in range(10):
            # 要消费
            rose = q.get()
            time.sleep(random.randint(0, 2))
            print(rose,"吃完了!")
    # 生产任务
    def make_rose(q):
        for i in range(10):
            # 再生产
            time.sleep(random.randint(0, 2))
            print("第%s盘青椒肉丝制作完成!" % i)
            rose = "第%s盘青椒肉丝" % i
            # 将生成完成的数据放入队列中
            q.put(rose)
    if __name__ == '__main__':
        # 创建一个共享队列
        q = Queue()
        make_p = Process(target=make_rose,args=(q,))
        eat_p =  Process(target=eat,args=(q,))

        make_p.start()
        eat_p.start()
    ```
    ​ 
    ​  
    ​  
     
    ​ 
    ​ 
     
     
     
     
     
     
     
     
  • 相关阅读:
    spring揭秘读书笔记----spring的ioc容器之BeanFactory
    spring启动加载过程源码分析
    java线程数过高原因分析
    spring揭秘读书笔记----ioc的基本概念
    git merge rebase的区别及应用场景
    spring实现定时任务
    jetty.xml解析
    Hackthebox--------irked
    CTF之信息泄漏
    CTF web题型解题技巧
  • 原文地址:https://www.cnblogs.com/llx--20190411/p/10970198.html
Copyright © 2020-2023  润新知