• 进程


    一. 操作系统

    1. 计算机的硬件组成

      主板 固化(寄存器,是直接和cpu进行交互的一个硬件)

      CPU 中央处理器:计算(数字计算和逻辑计算)和控制(控制所有硬件协调工作)

      存储 硬盘,内存

      输入设备 键盘,鼠标,话筒

      输出设备 显示器,音箱,打印机

    2. 操作系统

      早期的计算机是以计算为核心的, 现在的计算机是以存储为核心

      操作系统是一个软件, 是一个能直接操作硬件的软件.

      无论什么时候操作系统的目标总是: 让用户用起来更加轻松

      操作系统的作用: (1) 封装所有硬件接口,让各种用户使用电脑更加轻松

               (2) 是对计算机内所有的资源进行合理的调度和分配

    3. 多道技术:

        (1) 产生背景:针对单核,实现并发
        ps:
          现在的主机一般是多核,那么每个核都会利用多道技术.
          有4个cpu,运行于cpu1的某个程序遇到io阻塞,会等到io结束再重新调度,会被调度到4个
          cpu中的任意一个,具体由操作系统调度算法决定.
        (2) 空间上的复用:如内存中同时有多道程序.
        (3) 时间上的复用:复用一个cpu的时间片.
             强调:遇到io切,占用cpu时间过长也切,核心在于切之前将进程的状态保存下来,这样
                       才能保证下次切换回来时,能基于上次切走的位置继续运行.

    二. 进程的理论

      进程即程序正在执行的一个过程。进程是对正在运行的程序的一个抽象, 是系统进行资源分配和调度的基本单位,是操作系统结构的基础。。进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一。操作系统的其他所有内容都是围绕进程的概念展开的.

    1. 什么是进程

      (1) 进程的概念

    第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。
    文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。 第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。[
    3] 进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序的活动规律引进的一个概念,所有多道程序
    设计操作系统都建立在进程的基础上。

      (2) 进程的特征

    动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
    并发性:任何进程都可以同其他进程一起并发执行
    独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;
    异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进
    结构特征:进程由程序、数据和进程控制块三部分组成。
    多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。

      (3) 进程与程序的区别

    程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
    而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
    程序可以作为一种软件资料长期存在,而进程是有一定生命期的。
    程序是永久的,进程是暂时的。

    注意:同一个程序执行两次,就会在操作系统中出现两个进程,所以我们可以同时运行一个软件,分别做不同的事情也不会混乱.

    2. 进程的调度

      要想多个进程交替运行,操作系统必须对这些进程进行调度,这个调度也不是随机进行的,而是需要遵循一定的原则,由此就有了进程的调度算法。

      (1) 先来先服务调度算法

    先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。FCFS算法比较有利于长作业(进程),而不利于短作业(进程)。由此可知,
    本算法适合于CPU繁忙型作业,而不利于I/O繁忙型的作业(进程)。

      (2) 短作业优先调度算法

    短作业(进程)优先调度算法(SJ/PF)是指对短作业或短进程优先调度的算法,该算法既可用于作业调度,也可用于进程调度。但其对长作业不利;不能保证紧迫性作业(进程)
    被及时处理;作业的长短只是被估算出来的

      (3) 时间片轮转法

    时间片轮转(Round Robin,RR)法的基本思路是让每个进程在就绪队列中的等待时间与享受服务的时间成比例。在时间片轮转法中,需要将CPU的处理时间分成固定大小的时间片,例如,
    几十毫秒至几百毫秒。如果一个进程在被调度选中之后用完了系统规定的时间片,但又未完成要求的任务,则它自行释放自己所占有的CPU而排到就绪队列的末尾,等待下一次调度。同时,
    进程调度程序又去调度当前就绪队列中的第一个进程。   显然,轮转法只能用来调度分配一些可以抢占的资源。这些可以抢占的资源可以随时被剥夺,而且可以将它们再分配给别的进程。CPU是可抢占资源的一种。但打印机等资源是不可抢占的。
    由于作业调度是对除了CPU之外的所有系统硬件资源的分配,其中包含有不可抢占资源,所以作业调度不使用轮转法。 在轮转法中,时间片长度的选取非常重要。首先,时间片长度的选择会直接影响到系统的开销和响应时间。如果时间片长度过短,则调度程序抢占处理机的次数增多。这将使进程上下文切换次
    数也大大增加,从而加重系统开销。反过来,如果时间片长度选择过长,例如,一个时间片能保证就绪队列中所需执行时间最长的进程能执行完毕,则轮转法变成了先来先服务法。时间片长度
    的选择是根据系统对响应时间的要求和就绪队列中所允许最大的进程数来确定的。 在轮转法中,加入到就绪队列的进程有3种情况: 一种是分给它的时间片用完,但进程还未完成,回到就绪队列的末尾等待下次调度去继续执行。 另一种情况是分给该进程的时间片并未用完,只是因为请求I
    /O或由于进程的互斥与同步关系而被阻塞。当阻塞解除之后再回到就绪队列。 第三种情况就是新创建进程进入就绪队列。 如果对这些进程区别对待,给予不同的优先级和时间片从直观上看,可以进一步改善系统服务质量和效率。例如,我们可把就绪队列按照进程到达就绪队列的类型和进程被阻塞时的
    阻塞原因分成不同的就绪队列,每个队列按FCFS原则排列,各队列之间的进程享有不同的优先级,但同一队列内优先级相同。这样,当一个进程在执行完它的时间片之后,或从睡眠中被唤醒
    以及被创建之后,将进入不同的就绪队列。

     (4) 多级反馈队列 

    前面介绍的各种用作进程调度的算法都有一定的局限性。如短进程优先的调度算法,仅照顾了短进程而忽略了长进程,而且如果并未指明进程的长度,则短进程优先和基于进程长度的抢占式
    调度算法都将无法使用。 而多级反馈队列调度算法则不必事先知道各种进程所需的执行时间,而且还可以满足各种类型进程的需要,因而它是目前被公认的一种较好的进程调度算法。在采用多级反馈队列调度算法的
    系统中,调度算法的实施过程如下所述。 (
    1) 应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各
    不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第i+1个队列的时间片要比第i个队列的时间片长
    一倍。 (
    2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时
    尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一
    个长作业(进程)从第一队列依次降到第n队列后,在第n 队列便采取按时间片轮转的方式运行。 (
    3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进
    入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进
    程。

    3. 进程的并行与并发

       并行 : 并行是指两者同时执行,比如有两条车道,在某一个时间点,两条车道上都有车在跑;(资源够用,比如三个线程,四核的CPU )

      并发 : 并发是指资源有限的情况下,两者交替轮流使用资源,比如只有一条车道(单核CPU资源),那么就是A车先走,在某个时刻A车退出把道路让给B走,B走完继续给A ,                        交替使用,目的是提高效率。

      区别:

      并行 : 是从微观上,也就是在一个精确的时间片刻,有不同的程序在执行,这就要求必须有多个处理器。
      并发 : 是从宏观上,在一个时间段上可以看出是同时执行的,比如一个服务器同时处理多个session。

      注意:早期单核CPU时候,对于进程也是微观上串行(站在cpu角度看),宏观上并行(站在人的角度看就是同时有很多程序在执行)。

    4. 同步异步,阻塞非阻塞

      (1) 进程的状态

        在程序运行的过程中,由于被操作系统的调度算法控制,程序会进入三个状态:就绪,运行和阻塞。

      (1)就绪(Ready)状态 : 当进程已分配到除CPU以外的所有必要的资源,只要获得cpu便可立即执行,这时的进程状态称为就绪状态。

      (2)执行/运行(Running)状态 : 当进程已获得cpu,其程序正在cpu上执行,此时的进程状态称为执行状态。

      (3)阻塞(Blocked)状态 : 正在执行的进程,由于等待某个事件发生而无法执行时,便放弃cpu而处于阻塞状态, 此时,进程还在内存中。引起进程阻塞                                                的事件可有多种,例如,等待I/O完成(input)、申请缓冲区不能满足、等待信件(信号)等。

      特殊状态 :  是指因为种原因,进程放弃了cpu,导致进程无法继续执行,此时进程被踢出内存。

      (2) 同步和异步

    同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,
    两个任务的状态可以保持一致。
    异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是
    否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。

      (3) 阻塞和非阻塞

      阻塞就是做一件事情立刻得到了回答

      非阻塞就是你做一件事,但没有立刻得到回答,需要等待

      (4) 同步异步与阻塞非阻塞

      1) 同步阻塞形式

      效率最低.  你在排队等电梯,什么什么事也不做

      2) 异步阻塞形式

      在电梯那排队站着, 不用一直看电梯门开没开, 听到电梯门响,就知道电梯门开了.

      3) 同步非阻塞形式

      在排队等待电梯时, 出去打个电话, 隔一段时间来看一下电梯有没有开门. 

      4) 异步非阻塞形式

      效率最高

    三. 进程的操作 

    1.  开启进程的方式

    p = Process(target=函数名, args=(参数,))         args=((,)) 里面传参是以元组形式
    from multiprocessing import Process
    import time
    import os
    ############################################开启子进程的一种方式
    def func(i):
        time.sleep(1)
        print('这里是儿子进程,儿子自己的pid是%s,儿子的父进程的pid是%s'%(os.getpid(),os.getppid()))
    
    # os.getpid()获取的是当前进程自己的pid
    # os.getppid()获取的是当前进程的父进程的pid
    if __name__ == '__main__':
        p = Process(target=func,args=(1,))# 实例化一个进程对象
        p.start()# 开启一个子进程
        print('这里是父亲进程,父进程自己的pid是:%s,父亲的父亲的pid是%s'%(os.getpid(),os.getppid()))
    
    ######################################### 开启子进程的另外一种方式,以继承的方式
    
    class MyProcess(Process):
        def __init__(self):
            super(MyProcess, self).__init__()
        def run(self):
            print('这是以继承类的方式开启的子进程')
    
    if __name__ == '__main__':
        p1 = MyProcess()
        p1.start()# 是指,解释器告诉操作系统,去帮我开启一个进程,   就绪状态
        # p1.run()# 告诉操作系统,现在马上帮我执行这个子进程           执行

       开启多进程:

    from multiprocessing import Process
    import time
    import random
    
    def func(i):
        print('我是%s'%i)
    
    
    if __name__ == '__main__':
        l = []
        addr = ['河南的','山东的','辽宁的','湖南的']
        for i in addr:
            p = Process(target=func,args=(i,))
            p.start()
            # p.join()
            l.append(p)
    
        [p.join() for p in l]
        time.sleep(1)
        print('我选%s'%(random.choice(addr)))

    2. 进程的方法

    进程的方法:
             (1) start()    开启一个子进程, 底层调用的是p.run()
             (2) join()     异步变同步(就是让父进程停留在join这句话,等待子进程执行结束,父进程再继续执行)
             (3) is_alive() 判断进程是否活着
             (4) terminate()   杀死进程    
    from multiprocessing import Process
    import time
    
    def func():
        for i in range(500):
            time.sleep(0.01)
            print('儿子在这里')
    
    if __name__ == '__main__':
        p = Process(target=func)
        p.start()
        p.join()# 是让主进程等待子进程执行完。  现象:主进程执行到这句话,主进程阻塞住,等待子进程执行
        # time.sleep(1)
        for i in range(100):
            time.sleep(0.01)
            print('爸爸在这里')
    from multiprocessing import Process
    import time
    
    
    def func():
        time.sleep(1)
        print(123)
    
    
    if __name__ == '__main__':
        p = Process(target=func,)
        p.start()
        p.terminate()# 杀死p进程,让解释器告诉操作系统,请杀掉p进程。
        print('子进程是否还活着?', p.is_alive())
        time.sleep(0.002)
        print('子进程是否还活着?', p.is_alive())
        # 返回一个bool值,如果返回True,代表进程还活着,如果返回False,代表子进程死了

    3. 进程的属性

    进程的常用属性
           (1) p.name =    给p进程一个名字
           (2) p.pid       返回p进程的pid
           (3) p.daemon = True   将p进程设置为守护进程。(True为守护进程,False为普通进程)
               守护进程的两个特点:
                  守护进程会随着父进程的代码执行完毕而结束
                  守护进程不能再创建子进程
    from multiprocessing import Process
    import os
    
    def func():
        print('这里是儿子,儿子的pid是%s'%(os.getpid()))
    
    if __name__ == '__main__':
        p = Process(target=func)
        p.start()
        p.name = 'jack'
        print('儿子的名字是%s' % p.name)
        print('儿子的pid是%s' % p.pid)
        print('儿子是不是守护进程?', p.daemon)

      守护进程

    from multiprocessing import Process
    import time
    def func1():
        time.sleep(1)
        print('守护进程')
    def func2():
        time.sleep(1)
        print('普通进程')
    if __name__ == '__main__':
        p1 = Process(target=func1)
        p2 = Process(target=func2)
        p1.daemon = True  # 把p1设为守护进程
        p1.start()
        p2.start()
        p2.join()
        print('父进程')
    # 结果:
    # 守护进程
    # 普通进程
    # 父进程

    四. 进程间的通信

    1. 锁 : Lock

    l = Lock()   实例化一把锁, 一把锁配一把钥匙
    l.acquire()  拿钥匙,锁门 
    l.release()
    还钥匙,开门
    
    

    做一个买票的简单程序 10个人在同一时间去买3张票

    from multiprocessing import Value, Process
    import time
    def check(i, num):
        print('第%s个人查到余票还剩%s张' % (i, num.value))
    
    def buy(i, num):
    
        if num.value > 0:
            print('第%s个人抢到了票' % i)
            time.sleep(1)
            num.value -= 1
    
        else:
            print('第%s个人没有抢到票' % i)
    
    if __name__ == '__main__':
    
        num = Value('i', 3)
        for i in range(10):
            p1 = Process(target=check, args=(i + 1, num))
            p1.start()
        for i in range(10):
            p2 = Process(target=buy, args=(i + 1, num))
            p2.start()
    # 结果:      
    # 第2个人查到余票还剩3张
    # 第3个人查到余票还剩3张
    # 第6个人查到余票还剩3张
    # 第7个人查到余票还剩3张
    # 第1个人查到余票还剩3张
    # 第5个人查到余票还剩3张
    # 第10个人查到余票还剩3张
    # 第4个人查到余票还剩3张
    # 第8个人查到余票还剩3张
    # 第9个人查到余票还剩3张
    # 第1个人抢到了票
    # 第2个人抢到了票
    # 第3个人抢到了票
    # 第4个人抢到了票
    # 第5个人抢到了票
    # 第8个人抢到了票
    # 第9个人抢到了票
    # 第6个人抢到了票
    # 第7个人抢到了票
    # 第10个人抢到了票

    发现所有人都抢到票了,但是票却只有三张, 因为开的查票10个进程和买票10个进程都是异步执行的,买票的10个进程如果执行够快,还没有对余票数进行相应的操作,就会查到一直有余票3张,就可以买到票了.所以我们要让买票的进程一个个执行买票操作,而不是一起执行,而造成了数据混乱.

    from multiprocessing import Lock, Value,Process
    def check(i, num):
        print('第%s个人查到余票还剩%s张' % (i, num.value))
    
    def buy(i, num, l):
        l.acquire()  # 拿走钥匙,锁上门,不允许其进程进入
        if num.value > 0:
            print('第%s个人抢到了票' % i)
            num.value -= 1
    
        else:
            print('第%s个人没有抢到票' % i)
        l.release()  # 返还钥匙,其它进程可以进入一个
    
    if __name__ == '__main__':
        l = Lock()
        num = Value('i', 3)
        for i in range(10):
            p1 = Process(target=check, args=(i + 1, num))
            p1.start()
        for i in range(10):
            p2 = Process(target=buy, args=(i + 1, num, l))
            p2.start()
    # 结果:
    # 第1个人查到余票还剩3张
    # 第2个人查到余票还剩3张
    # 第3个人查到余票还剩3张
    # 第4个人查到余票还剩3张
    # 第5个人查到余票还剩3张
    # 第7个人查到余票还剩3张
    # 第6个人查到余票还剩3张
    # 第8个人查到余票还剩3张
    # 第9个人查到余票还剩3张
    # 第10个人查到余票还剩3张
    # 第1个人抢到了票
    # 第2个人抢到了票
    # 第3个人抢到了票
    # 第4个人没有抢到票
    # 第5个人没有抢到票
    # 第6个人没有抢到票
    # 第7个人没有抢到票
    # 第8个人没有抢到票
    # 第9个人没有抢到票
    # 第10个人没有抢到票

    2. 信号量: Semaphore

    sem = Semaphore(n)
            n : 是指初始化一把锁配几把钥匙,一个int型
            拿钥匙,锁门  l.acquire()
            还钥匙,开门  l.release()
            信号量机制比锁机制多了一个计数器,这个计数器是用来记录当前剩余几把钥匙的。
            当计数器为0时,表示没有钥匙了,此时acquire()处于阻塞。
            对于计数器来说,每acquire一次,计数器内部就减1,release一次,计数器就加1
    from multiprocessing import Process, Semaphore
    import time
    def func(i, se):
        se.acquire()  # 可以计入5个进程
        print(i)
        time.sleep(5)
        se.release()  # 还一把钥匙,进来一个进程
    
    
    if __name__ == '__main__':
        se = Semaphore(5)  # 5把钥匙
        for i in range(10):  # 开10个进程
            p = Process(target=func, args=(i, se))
            p.start()

    3. 事件机制 : Event

     e = Event()
            # e.set()    #将is_set()设为True
            # e.clear()  # 将is_set()设为False
            # e.wait()   #判断is_set的bool值,如果bool为True,则非阻塞,bool值为False,则阻塞
            # e.is_set() # 标识
            # 事件是通过is_set()的bool值,去标识e.wait() 的阻塞状态
            # 当is_set()的bool值为False时,e.wait()是阻塞状态
            # 当is_set()的bool值为True时,e.wait()是非阻塞状态
            # 当使用set()时,是把is_set的bool变为True
            # 当使用clear()时,是把is_set的bool变为False
    from multiprocessing import Event, Process
    import time
    def traffic_light(e):
        e.set()  # # 把e.is_set() 设置为True
        print('33[32m greenlight! 33[0m')  # 33[32m 数据 33[0m 设置数据显示的颜色为绿色
        while 1:
            if e.is_set():
                time.sleep(0.5)
                print('33[31m redlight 33[0m')  # 33[31m 数据 33[0m 设置数据显示的颜色为红色
                e.clear()  # 把e.is_set() 设置为False
            else:
                time.sleep(0.5)
                print('33[32m greenlight 33[0m')
    
                e.set()  # 把e.is_set() 设置为True
    
    def car(i, e):
        e.wait()
        print('第%s辆车过去了' % i)
    
    if __name__ == '__main__':
        e = Event()
        trlt = Process(target=traffic_light, args=(e,))
        trlt.start()
        for i in range(100):  # 100辆车
            p = Process(target=car, args=(i+1, e))
            p.start()

    4. 队列 : queue  实现生产者消费者模型

    队列: 先进先出(First In First Out   简称 FIFO)
    
    
        import queue  # 不能进行多进程之间的数据传输
        (1)from multiprocessing import Queue   借助Queue解决生产者消费者模型
           队列是安全的。
           q = Queue(num)
           num : 队列的最大长度
           q.get()# 阻塞等待获取数据,如果有数据直接获取,如果没有数据,阻塞等待
           q.put()# 阻塞,如果可以继续往队列中放数据,就直接放,不能放就阻塞等待
    
           q.get_nowait()# 不阻塞,如果有数据直接获取,没有数据就报错
           q.put_nowait()# 不阻塞,如果可以继续往队列中放数据,就直接放,不能放就报错
    2)from multiprocessing import JoinableQueue#可连接的队列
           JoinableQueue是继承Queue,所以可以使用Queue中的方法
           并且JoinableQueue又多了两个方法
           q.join()# 用于生产者。等待 q.task_done的返回结果,通过返回结果,生产者就能获得消费者当前消费了多少个数据
           q.task_done() # 用于消费者,是指每消费队列中一个数据,就给join返回一个标识。

      实现生产者消费者模型方法一 :

    from multiprocessing import Queue,Process
    def consumer(q, name):
        while 1:
            info = q.get()
            if info:
                print(name + '买了' + info)
            else:
                # print('卖完了')
                break
    
    
    def producer(q, product):
        for i in range(30):
            info = '第%s批的%s号商品' % (product, i)
            q.put(info)
    
    
    if __name__ == '__main__':
        q = Queue(10)  # 容量为10的队列
    
        p_pro1 = Process(target=producer, args=(q, '1'))
        p_pro2 = Process(target=producer, args=(q, '2'))
        p_pro3 = Process(target=producer, args=(q, '3'))
    
        p_con1 = Process(target=consumer, args=(q, 'jack'))
        p_con2 = Process(target=consumer, args=(q, 'tom'))
        p_con3 = Process(target=consumer, args=(q, 'ben'))
    
        p_l = [p_con1, p_con2, p_con3, p_pro1, p_pro2, p_pro3]  # 消费者在前
        [p.start() for p in p_l]  # 开启六个进程
        p_pro1.join()  # 等待生产者生产完
        p_pro2.join()
        p_pro3.join()
        q.put(None)  # 有多少个消费者传多少个None
        q.put(None)
        q.put(None)

      实现生产者消费者模型方法二:

    from multiprocessing import Process, JoinableQueue
    # q.join()# 用于生产者。等待 q.task_done的返回结果,通过返回结果,生产者就能获得消费者当前消费了多少个数据
    # q.task_done() # 用于消费者,是指每消费队列中一个数据,就给join返回一个标识。
    
    # 假设生产者生产了100个数据,join就能记录下100这个数字。每次消费者消费一个数据,就必须要task_done返回一个标识,当生产者(join)接收到100个消费者返回来的标识的时候,
    生产者就能知道消费者已经把所有数据都消费完了。
    def consumer(q, name, color): while 1: info = q.get() print('%s %s 拿走了%s 33[0m' % (color, name, info)) q.task_done() # 消费者如何判断,生产者是没来得及生产数据,还是生产者不再生产数据了? # 如果你尝试用get_nowait() + try 的方式去尝试获得生产者不再生产数据,此时是有问题的。 def producer(q, product): for i in range(20): info = product + '的产品%s号' % str(i) q.put(info) q.join() # 记录了生产了20个数据在队列中,此时会阻塞等待消费者消费完队列中所有数据 if __name__ == '__main__': q = JoinableQueue(10) p_pro1 = Process(target=producer, args=(q, '第一批')) p_con1 = Process(target=consumer, args=(q, 'jack', '33[31m')) p_con1.daemon = True # 把消费者进程设为守护进程 p_con1.start() p_pro1.start() p_pro1.join() # 主进程等待生产者进程结束 # 程序有3个进程,主进程和生产者进程和消费者进程。 当主进程执行到35行代码时,主进程会等待生产进程结束 # 而生产进程中(第26行)会等待消费者进程把所有数据消费完,生产者进程才结束。 # 现在的状态就是 主进程等待生产者进程结束,生产者进程等待消费者消费完所有数据 # 所以,把消费者设置为守护进程。 当主进程执行完,就代表生产进程已经结束,也就代表消费者进程已经把队列中数据消费完 # 此时,主进程一旦结束,守护进程也就是消费者进程也就跟着结束。 整个程序也就能正常结束了。

    5.  进程池

    进程池:一个池子,里边有固定数量的进程。这些进程一直处于待命状态,一旦有任务来,马上就有进程去处理。
           因为在实际业务中,任务量是有多有少的,如果任务量特别的多,不可能要开对应那么多的进程数
           开启那么多进程首先就需要消耗大量的时间让操作系统来为你管理它。其次还需要消耗大量时间让
           cpu帮你调度它。
           进程池还会帮程序员去管理池中的进程。
           from multiprocessing import Pool
           p = Pool(os.cpu_count() + 1)
    
           进程池有三个方法:
             map(func,iterable)
             func:进程池中的进程执行的任务函数
             iterable: 可迭代对象,是把可迭代对象中的每个元素依次传给任务函数当参数
    
             apply(func,args=()): 同步的效率,也就是说池中的进程一个一个的去执行任务
             func:进程池中的进程执行的任务函数
             args: 可迭代对象型的参数,是传给任务函数的参数
             同步处理任务时,不需要close和join
             同步处理任务时,进程池中的所有进程是普通进程(主进程需要等待其执行结束)
    
             apply_async(func,args=(),callback=None): 异步的效率,也就是说池中的进程一次性都去执行任务
             func:进程池中的进程执行的任务函数
             args: 可迭代对象型的参数,是传给任务函数的参数
             callback: 回调函数,就是说每当进程池中有进程处理完任务了,返回的结果可以交给回调函数,由回调函数进行进一步的处理,回调函数只有异步才有,同步是没有的
             异步处理任务时,进程池中的所有进程是守护进程(主进程代码执行完毕守护进程就结束)
             异步处理任务时,必须要加上close和join
    
             回调函数的使用:
                 进程的任务函数的返回值,被当成回调函数的形参接收到,以此进行进一步的处理操作
                 回调函数是由父进程调用的,而不是子进程,子进程只负责把结果传递给回调函数

      1) map

       map(func, [结果, for 变量 in 可迭代对象])
    from multiprocessing import Pool
    import os
    
    def func(num):
        num += 1
        # print(num)
        return num
    
    if __name__ == '__main__':
        p = Pool(os.cpu_count() + 1)  # 开启 (os.cpu_count() + 1) 个进程
        res = p.map(func, [i for i in range(10)])
        p.close()
        p.join()
        print('主进程中map的返回值', res)
    # 主进程中map的返回值 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

      2) apply(同步)

    from multiprocessing import Pool
    import time, os
    
    def func(num):
        num += 1
        return num
    
    if __name__ == '__main__':
        p = Pool(os.cpu_count() + 1)
        start = time.time()
        for i in range(10000):
            res = p.apply(func,args=(i,))# 同步处理这100个任务,同步是指,哪怕我进程中有5个进程,也依旧是1个进程1个进程的去执行任务
            # time.sleep(0.5)
            print(res)
        print(time.time() - start)

      3) apply_async(异步)

    from multiprocessing import Pool
    import time
    
    def func(num):
        time.sleep(0.5)
        print(num+1)
    
    if __name__ == '__main__':
        p = Pool(5)
        for i in range(100):
            p.apply_async(func, args=(i,))
            # time.sleep(0.5)
        p.close()
        p.join()

      回调函数 : 异步才有

    from multiprocessing import Pool
    import requests
    import time,os
    
    def func(url):
        res = requests.get(url)
        print('子进程的pid:%s,父进程的pid:%s'%(os.getpid(),os.getppid()))
        # print(res.text)
        if res.status_code == 200:
            return url,res.text
    
    def cal_back(sta):
        url, text = sta
        print('回调函数的pid', os.getpid())
        with open('a.txt','a',encoding='utf-8') as f:
            f.write(url + text)
        # print('回调函数中!',url)
    
    if __name__ == '__main__':
        p = Pool(5)
        l = ['https://www.baidu.com',
             'http://www.jd.com',
             'http://www.taobao.com',
             'http://www.mi.com',
             'http://www.cnblogs.com',
             'https://www.bilibili.com',
             ]
        print('主进程的pid',os.getpid())
        for i in l:
            p.apply_async(func, args=(i,),callback=cal_back)#
            # 异步执行任务func,每有一个进程执行完任务后,在func中return一个结果,结果会自动的被callback指定的函数,当成形式参数来接收到
        p.close()
        p.join()

    6. 进程间共享内存

    from multiprocessing import Process
    def func(num):
        num -= 1
        print('子进程', num)  # 子进程 19
        
    if __name__ == '__main__':
        num = 20
        p = Process(target=func, args=(num,))
        p.start()
        p.join()
        print(num)  # 20

      把父进程的num传给子进程,子进程对其进行操作,但是父进程的num值却没有改变,这是因为进程间相互独立,不共享内存,可以通过Manager和Value来解决.

      1) Manager

    from multiprocessing import Process,Manager
    def func(num):
        num[0] -= 1
        print('子进程中的num的值是',num)
    
    if __name__ == '__main__':
        m = Manager()
        num = m.list([1,2,3])
        p = Process(target=func,args=(num,))
        p.start()
        p.join()
        print('父进程中的num的值是',num)
        
    # 子进程中的num的值是 [0, 2, 3]
    # 父进程中的num的值是 [0, 2, 3]

      2) Value

        Value(数据类型,  数据)

    from multiprocessing import Process,Value
    def func(num):
        num.value -= 1
        print('子进程', num.value)  # 子进程 19
    
    if __name__ == '__main__':
        num = Value('i', 20)  # 'i'表示int型,20是数据
        print(num)  # <Synchronized wrapper for c_long(20)>
        print(num.value)  # 20
        p = Process(target=func, args=(num,))
        p.start()
        p.join()
        print(num.value)  # 19

     

  • 相关阅读:
    项目计划进度控制与资源管理
    读大道至简所思
    Java自学第三十二天
    Java自学第三十一天
    Java自学第三十天
    Java自学第二十九天
    Java自学第二十八天
    Java自学第二十七天
    Java自学第二十六天
    Java自学第二十五天
  • 原文地址:https://www.cnblogs.com/huangqihui/p/9507933.html
Copyright © 2020-2023  润新知