zoukankan      html  css  js  c++  java
  • python之多进程

    一.概述

    1.什么是进程

    进程:正在进行的一个过程或者说一个任务。而负责执行任务则是cpu。

    2.进程与程序区别

    程序仅仅只是一堆代码而已,而进程指的是程序的运行过程。

    3.并发与并行

    无论是并行还是并发,在用户看来都是'同时'运行的,不管是进程还是线程,都只是一个任务而已,真是干活的是cpu,cpu来做这些任务,而一个cpu同一时刻只能执行一个任务。

    并发:是伪并行,即看起来是同时运行。单个cpu+多道技术就可以实现并发。

    并行:同时运行,只有具备多个cpu才能实现并行。

    4.进程的层次结构

    无论UNIX还是windows,进程只有一个父进程,不同的是:

    在UNIX中所有的进程,都是以init进程为根,组成树形结构。父子进程共同组成一个进程组,这样,当从键盘发出一个信号时,该信号被送给当前与键盘相关的进程组中的所有成员。

    在windows中,没有进程层次的概念,所有的进程都是地位相同的,唯一类似于进程层次的暗示,是在创建进程时,父进程得到一个特别的令牌(称为句柄),该句柄可以用来控制子进程,但是父进程有权把该句柄传给其他子进程,这样就没有层次了。

    5.进程的状态

    在两种情况下会导致一个进程在逻辑上不能运行:

    1.进程挂起是自身原因,遇到I/O阻塞,便要让出CPU让其他进程去执行,这样保证CPU一直在工作。

    2.与进程无关,是操作系统层面,可能会因为一个进程占用时间过多,或者优先级等原因,而调用其他的进程去使用CPU。

    因而一个进程由三种状态

    二.进程的使用

    1.multiprocessing模块介绍

    python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu\_count()查看),在python中大部分情况需要使用多进程。

    Python提供了multiprocessing。 multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,>提供了Process、Queue、Pipe、Lock等组件。

    需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。

    2.process类介绍

    创建进程的类:

    Process([group [, target [, name [, args [, kwargs]]]]])

    由该类实例化得到的对象,可用来开启一个子进程

    强调: 1. 需要使用关键字的方式来指定参数

        2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

    参数介绍

    group参数未使用,值始终为None
    target表示调用对象,即子进程要执行的任务
    args表示调用对象的位置参数元组,args=(1,2,'egon',)
    kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
    name为子进程的名称

    方法介绍:

    p.start():启动进程,并调用该子进程中的p.run() 
    p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法  
    p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
    p.is_alive():如果p仍然运行,返回True
    p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间。

    属性介绍:

    p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
    p.name:进程的名称
    p.pid:进程的pid

    3.开启进程的两个方法

    方法一

    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__':
        # Process(target=task, kwargs={'name':'子进程1'})
        p = Process(target=task, args=('子进程1',))
        p.start()  # 仅仅只是发送一个信号
    
        print('')

    方法二

    from multiprocessing import Process
    import time
    
    
    class MyProcss(Process):
        def __init__(self, name):
            super().__init__()
            self.name = name
    
        def run(self):
            print('%s is running' % self.name)
            time.sleep(3)
            print('%s is done' % self.name)
    
    
    if __name__ == '__main__':
        p = MyProcss('子进程1')
        p.start()
    
        print('')

    4.join方法和守护进程

    a.join

    在主进程运行过程中如果想并发地执行其他的任务,我们可以开启子进程,此时主进程的任务与子进程的任务分两种情况。

    情况一:在主进程的任务与子进程的任务彼此独立的情况下,主进程的任务先执行完毕后,主进程还需要等待子进程执行完毕,然后统一回收资源。

    情况二:如果主进程的任务在执行到某一个阶段时,需要等待子进程执行完毕后才能继续执行,就需要有一种机制能够让主进程检测子进程是否运行完毕,在子进程执行完毕后才继续执行,否则一直在原地阻塞,这就是join方法的作用。

    from multiprocessing import Process
    import time,os
    
    
    def task(name):
        print('%s is running,parent id is %s' % (os.getpid(), os.getppid()))
        time.sleep(3)
        print('%s is done,parent id is %s' % (os.getpid(), os.getppid()))
    
    
    if __name__ == '__main__':
        p = Process(target=task, args=('子进程1',))
        p.start()  # 仅仅只是发送一个信号
    
        p.join()  # 等待子进程结束
        print('', os.getpid(), os.getppid())

    b.守护进程

    主进程创建子进程,然后将该进程设置成守护自己的进程,守护进程就好比崇祯皇帝身边的老太监,崇祯皇帝已死老太监就跟着殉葬了。

    关于守护进程需要强调两点:

    其一:守护进程会在主进程代码执行结束后就终止

    其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

    适用场景:

    如果我们有两个任务需要并发执行,那么开一个主进程和一个子进程分别去执行就ok了,如果子进程的任务在主进程任务结束后就没有存在的必要了,那么该子进程应该在开启前就被设置成守护进程。主进程代码运行结束,守护进程随即终止。

    from multiprocessing import Process
    import time
    
    
    def task(name):
        print('%s is running' % name)
        time.sleep(2)
    
    
    if __name__ == '__main__':
        p = Process(target=task, args=('子进程1',))
        p.daemon = True  # 子进程还没启动就随主关闭
        p.start()
        print('')

    5.互斥锁

    进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,而共享带来的是竞争,竞争带来的结果就是错乱,如下代码:

    #并发运行,效率高,但竞争同一打印终端,带来了打印错乱
    from multiprocessing import Process
    import os,time
    def work(): print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid())
    if __name__ == '__main__': for i in range(3): p=Process(target=work) p.start()

    加锁处理来控制输出

    #由并发变成了串行,牺牲了运行效率,但避免了竞争
    from multiprocessing import Process,Lock
    import os,time
    def work(lock): lock.acquire() #加锁 print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid()) lock.release() #释放锁

    if __name__ == '__main__': lock=Lock() for i in range(3): p=Process(target=work,args=(lock,)) p.start()

    6.队列

    进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的。

    from multiprocessing import Queue
    
    q = Queue(3)
    
    q.put('hello')
    q.put({'a': 1})
    q.put([3, 3, 3])
    print(q.full())
    # q.put(4) #再放就阻塞住了
    
    print(q.get())
    print(q.get())
    print(q.get())
    print(q.empty())  # 空了
    # print(q.get()) #再取就阻塞住了
  • 相关阅读:
    php数组
    php 函数
    数据库操作
    PHP基础
    mysql常用函数
    10.25 (下午) 开课一个月零二十一天(抽象)
    10.25 (上午) 开课一个月零二十一天 (继承多态)
    10.24 (下午) 开课一个月零二十天 (封装)
    10.24 (上午) 开课一个月零二十天 (面向对象)
    10.23 开课一个月零十九天 (PHP数组)
  • 原文地址:https://www.cnblogs.com/fanhk/p/9286512.html
Copyright © 2011-2022 走看看