zoukankan      html  css  js  c++  java
  • 进程

    守护进程

    什么是守护进程:进程是一个正在运行的程序,守护进程也是一个普通进程,

    意思是一个进程可以守护另一个进程。

    结论:如果说b是a的守护进程,那么a就是被守护的进程,如果a挂了,那b也没有什么必要继续存在了。

    测试:

    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,那么这个子进程也就关闭了。

    互斥锁:

    就是互相排斥的锁,也就是如果这个资源被锁了,那么其他进程就无法使用了。

    需要强调的是,锁并不是真正把资源锁起来了,而是限制了代码的执行。

    为什么需要互斥锁:因为需要解决并发带来的资源竞争问题,当多个进程要同时操作同一个资源时,将会导致数据错乱的问题。

    解决问题:

    1.加join,这样就解决了数据错乱的问题,但是效率降低了,而且完全把并发变成串行了,也没有必要开子进程了,另外加join也等于把进程执行的顺序给定死了,这样太不公平了。

    2.就是给公共资源上互斥锁,这样也解决了数据错乱,把并发变为串行,另外也很公平谁先抢到谁先执行。

    锁和join的区别:

    1.join固定了执行顺序,会造成父进程等子进程。锁依然公平竞争谁抢到谁先执行,而且父进程也可以做自己的事。

    最主要的区别: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 解锁

    注意:不要对同一把锁执行多个acquire 会锁死导致程序无法执行,一次锁对应一次开锁。

    注意2:想要数据保证完全,必须所有进程用同一把锁。

    IPC:

    进程间通讯

    通讯指的就是交换数据

    进程之间内存是相互隔离的,当一个进程想要把数据给另外一个进程,就需要考虑IPC

    方式:

    管道: 只能单向通讯,数据都是二进制

    文件: 在硬盘上创建共享文件

    缺点:速度慢

    优点:数据量几乎没有限制

    socket:

    编程复杂度较高

    共享内存:必须由操作系统来分配 要掌握的方式*

    优点: 速度快

    缺点: 数据量不能太大

    共享内存的方式:

    1.Manager类:

    Manager提供很多数据结构 list dict等等

    Manager所创建出来的数据结构,具备进程间共享的特点

    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队列帮我们处理了锁的问题:

    队列是一种特殊的数据结构,先存储的先取出 就像排队 先进先出

    相反的是堆栈,先存储的后取出, 就像衣柜 桶装薯片 先进后出

    扩展:

    函数嵌套调用时 执行顺序是先进后出 也称之为函数栈

    调用 函数时 函数入栈 函数结束就出栈

    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有效

    生产者消费者模型:

    模型 就是解决某个问题套路

    产生数据的一方称之为生产者

    处理数据的一方称之为消费者

    例如: 爬虫 生活中到处都是这种模型

    饭店 厨师就是生产者 你吃饭的人就是消费者

    生产者和消费者出什么问题解决什么问题。

    生产者和消费者处理速度不平衡,一方快一方慢,导致一方需要等待另一方。

    原本双方是耦合的,在一起,消费者必须等待生产者生成完毕再开始处理。

    反过来:如果消费者消费速度太慢,生产者必须等待其处理完毕才能开始生成下一个数据。

    解决的方案:

    将双方分开来.一专门负责生成,一方专门负责处理

    这样一来数据就不能直接交互了 双方需要一个共同的容器

    生产者完成后放入容器,消费者从容器中取出数据

    这样就解决了双发能力不平衡的问题,做的快的一方可以继续做,不需要等待另一方

    案例:

    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()
  • 相关阅读:
    Ajax 学习笔记
    StringBulider简单用法
    asp.net 发送邮件
    log4net日志组件
    Web.Config文件详解
    关闭discuzX3.2注册页面的注册邮箱验证
    Oracle 序列(sequence)的创建、修改及删除
    MySQL 和 Oracle 主键自增长
    EL(表达式)语言的几种运算符
    SQL:select case when 的用法
  • 原文地址:https://www.cnblogs.com/xinfan1/p/10974261.html
Copyright © 2011-2022 走看看