Learning-Python【33】:并发编程之多进程

一、multiprocessing 模块介绍

Python 中的多线程无法利用多核优势,如果想要充分地使用多核 CPU 的资源(os.cpu_count()查看),在 Python 中大部分情况需要使用多进程。Python提供了 multiprocessing。

multiprocessing 模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块 threading 的编程接口类似。

multiprocessing 模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了 Process、Queue、Pipe、Lock 等组件。

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

二、Process 类的介绍

1、创建进程的类

Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,可用来开启一个子进程

强调:
1. 需要使用关键字的方式来指定参数
2. args 指定的为传给 target 函数的位置参数,是一个元组形式,必须有逗号

2、参数介绍

group参数未使用,值始终为None

target表示调用对象,即子进程要执行的任务

args表示调用对象的位置参数元组,args=(1,2,‘qiu‘,)

kwargs表示调用对象的字典,kwargs={‘name‘:‘qiu‘,‘age‘:18}

name为子进程的名称

3、方法介绍

p.start():启动进程,并调用该子进程中的 p.run()
p.run():进程启动时运行的方法,正是它去调用 target 指定的函数,我们自定义类的类中一定要实现该方法  

p.terminate(): 强制终止进程 p,不会进行任何清理操作,如果 p 创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。
如果 p 还保存了一个锁那么也将不会被释放,进而导致死锁
p.is_alive(): 如果 p 仍然运行,返回 True

p.join([timeout]): 主线程等待 p 终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,
需要强调的是,p.join只能join住 start 开启的进程,而不能 join 住 run 开启的进程

4、属性介绍

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

p.name: 进程的名称

p.pid:进程的pid

p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)

p.authkey: 进程的身份验证键,默认是由 os.urandom() 随机生成的 32 字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)

三、Process类的使用

注意:在 Windows 中 Process() 必须放到 if __name__ == ‘__main__‘: 下

Since Windows has no fork, the multiprocessing module starts a new Python
process and imports the calling module.
    If Process() gets called upon import, then this sets off an infinite succession of
new processes (or until your machine runs out of resources).
    This is the reason for hiding calls to Process() inside

    if __name__ == "__main__"
    since statements inside this if-statement will not get called upon import.
    由于Windows没有fork,多处理模块启动一个新的Python进程并导入调用模块。
    如果在导入时调用Process(),那么这将启动无限继承的新进程(或直到机器耗尽资源)。
这是隐藏对Process()内部调用的原,使用if __name__ == “__main __”,这个if语句
中的语句将不会在导入时被调用。

创建并开启子进程的两种方式

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__‘:
    p = Process(target=task, args=("qiu",))
    # p = Process(target=task, kwargs={"name": "qiu"})

    # p.start()只是向操作系统发送了一个开启子进程的信号, 操作系统才能开启子进程,
    # 涉及到申请内存空间, 要将父进程的数据拷贝到子进程, 要将CPU调到子进程里运行子进程的代码
    # 才会有 is running的显示, 这都是一系列的硬件操作
    # 所以print("主")这行代码运行速度要快一些
    p.start()
    print("主")

方式一

from multiprocessing import Process
import time

class MyProcess(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 = MyProcess("qiu")
    p.start()
    print("主")

方式二

四、join方法

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

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

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

from multiprocessing import Process
import time

def task(name, n):
    print("%s is running" %name)
    time.sleep(n)
    print("%s is done" %name)

if __name__ == ‘__main__‘:
    p1 = Process(target=task, args=("Process 1", 1))
    p2 = Process(target=task, args=("Process 2", 2))
    p3 = Process(target=task, args=("Process 3", 3))

    start = time.time()
    p1.start()
    p2.start()
    p3.start()

    p1.join()
    p2.join()
    p3.join()
    print("主进程", time.time() - start)

join

  人会有疑问,既然 join 是等待进程结束,那么我像下面 join 下去,进程不就变成串行了的吗?

  当然不是了,必须明确 join 是让谁等:进程只要 start 就会在开始运行了,所以 p1 到 p3.start() 时,系统中已经有三个并发的进程了,而 p1.join() 是在等 p1 结束,p1 只要不结束主线程就会一直卡在原地,这也是问题的关键。join 是让主线程等,而 p1-p3 仍然是并发执行的,p1.join() 的时候,其余 p2,p3 仍然在运行,等 p1.join() 结束,可能 p2,p3 早已经结束了,这样 p2.join(),p3.join() 直接通过检测,无需等待。所以 3 个 join 花费的总时间仍然是耗费时间最长的那个进程运行的时间,所以这里即便交换 join 的顺序,执行的时间仍然是 3 秒多一点,多出来的那零点几秒是开启进程以及进程切换的时间。

from multiprocessing import Process
import time

def task(name, n):
    print("%s is running" %name)
    time.sleep(n)
    print("%s is done" %name)

if __name__ == ‘__main__‘:
    p1 = Process(target=task, args=("Process 1", 1))
    p2 = Process(target=task, args=("Process 2", 2))
    p3 = Process(target=task, args=("Process 3", 3))

    start = time.time()
    p1.start()
    p2.start()
    p3.start()

    p3.join()
    p1.join()
    p2.join()

    print("主进程", time.time() - start)

交换join的顺序

join 是让主进程在原地等待,等待子进程运行完毕,不会影响子进程的执行

上面的代码可以使用 for 循环简写

from multiprocessing import Process
import time

def task(name, n):
    print("%s is running" %name)
    time.sleep(n)
    print("%s is done" %name)

if __name__ == ‘__main__‘:

    start = time.time()
    p_l = []
    for i in range(1, 4):
        p = Process(target=task, args=("Process %s" %i, i))
        p_l.append(p)
        p.start()

    for p in p_l:
        p.join()

    print("主进程", time.time() - start)

使用for循环简写

进程间的内存空间互相隔离

from multiprocessing import Process

n = 100

def task():
    global n
    n = 0

if __name__ == ‘__main__‘:
    p = Process(target=task)
    p.start()
    p.join()
    print("主进程内的:", n)

僵尸进程与孤儿进程

  僵尸进程:一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程

  我们知道在 Unix/Linux 中,正常情况下子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束,如果子进程一结束就立刻回收其全部资源,那么在父进程内将无法获取子进程的状态信息。因此,Unix 提供了一种机制可以保证父进程可以在任意时刻获取子进程结束时的状态信息:

  1、在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号、退出状态、运行时间等)

  2、直到父进程通过 wait/waitpid 来取时才释放。但这样就导致了问题,如果进程不调用 wait/waitpid 的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。此即为僵尸进程的危害,应当避免。

  任何一个子进程(init除外)在 exit() 之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在 exit() 之后,父进程没有来得及处理,这时用 ps 命令就能看到子进程的状态是 “Z” 。如果父进程能及时 处理,可能用 ps 命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。  如果父进程在子进程结束之前退出,则子进程将由 init 接管。init 将会以父进程的身份对僵尸状态的子进程进行处理。

  孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。

  孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了 init 进程身上,init 进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为 init,而 init 进程会循环地 wait() 它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init 进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。

原文地址:https://www.cnblogs.com/qiuxirufeng/p/9925744.html

时间: 2024-07-30 17:10:21

Learning-Python【33】:并发编程之多进程的相关文章

Python 3 并发编程多进程之队列(推荐使用)

Python 3 并发编程多进程之队列(推荐使用) 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 可以往队列里放任意类型的数据 创建队列的类(底层就是以管道和锁定的方式实现): 1 Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递. 参数介绍: 1 maxsize是队列中允许最大项数,省略则无大小限制. 方法介绍: 1.主要

Python 3 并发编程多进程之进程同步(锁)

Python 3 并发编程多进程之进程同步(锁) 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,竞争带来的结果就是错乱,如何控制,就是加锁处理. 1.多个进程共享同一打印终端 from multiprocessing import Process import os,time def work(): print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.g

python并发编程之多进程

python并发编程之多进程 一.什么是进程 进程:正在进行的一个过程或者一个任务,执行任务的是CPU. 原理:单核加多道技术 二.进程与程序的区别 进程是指程序的运行过程 需要强调的是:同一个程序执行两次是两个进程,比如打开暴风影音,虽然都是同一个软件,但是一个可以播放苍井空,另一个可以播放武藤兰. 三.并发与并行 无论是并行还是并发,在用户看来都是'同时'运行的,不管是进程还是线程,都只是一个任务而已,真是干活的是cpu,cpu来做这些任务,而一个cpu同一时刻只能执行一个任务. (1)并发

进程,操作系统,Python并发编程之多进程

1.进程基础知识 1.程序:若干文件 2.进程:一个正在执行的文件,程序 3.进程被谁执行:cpu最终运行指定的程序 4.操作系统调度作用:将磁盘上的程序加载到内存,然后交由CPU去处理,一个CPU正在运行的一个程序,就叫开启了一个进程 2.操作系统 1.操作系统:存在于硬盘与软件之间,管理.协调.控制软件与硬件的交互 2.操作系统的作用:将一些复杂的硬件封装成简单的借口,便于使用;合理地调度分配多个进程与cpu的关系,让其有序化 3.操作系统发展史 ①第一代电子计算机(1940-1955) 二

python并发编程02/多进程

目录 python并发编程02/多进程 1.进程创建的两种方式 1.1开启进程的第一种方式 1.2开启进程的第二种方式 1.3简单应用 2.进程pid 2.1命令行获取所有的进程的pid tasklist 2.2代码级别如何获取一个进程的pid 2.3获取父进程(主进程)的pid 3.验证进程之间的空间隔离 4.进程对象join方法 5.进程对象其他属性 6.守护进程 python并发编程02/多进程 1.进程创建的两种方式 1.1开启进程的第一种方式 from multiProcessing

python之并发编程

一.操作系统 1.概念 操作系统就是一个协调.管理和控制计算机硬件资源和软件资源的控制程序,操作系统位于计算机硬件与应用软件之间,本质也是一个软件. 操作系统由操作系统的内核(运行于内核态,管理硬件资源)以及系统调用(运行于用户态,为应用程序员写的应用程序提供系统调用接口)两部分组成 现代计算机或者网络都是多用户的,多个用户不仅共享硬件,而且共享文件,数据库等信息,共享意味着冲突和无序. 2.操作系统功能 1.记录哪个程序使用什么资源 2.对资源请求进行分配 3.为不同的程序和用户调解互相冲突的

并发编程之多进程

#Python 并发编程之多进程##1.1 multiprocessing 模块 Python 中的多线程无法利用多核资源,如果想要充分的使用多核 cpu 的资源,在 Python 中大部分情况需要使用多进程. Python 为我们提供了非常好用的多进程包multiprocessing! os.cpu_coutn() multiprocessing模块用来开启子进程,并在模块中执行我们定制的任务(如函数,实现功能等). multiprocessing 模块的功能有很多: ?支持子进程 ?通信和共

并发编程之多进程进程进程

Python 并发编程之多进程 1.1 multiprocessing 模块 Python 中的多线程无法利用多核资源,如果想要充分的使用多核 cpu 的资源,在 Python 中大部分情况需要使用多进程. Python 为我们提供了非常好用的多进程包multiprocessing! os.cpu_coutn() multiprocessing模块用来开启子进程,并在模块中执行我们定制的任务(如函数,实现功能等). multiprocessing 模块的功能有很多: ?支持子进程 ?通信和共享数

python-学习-python并发编程之多进程与多线程

一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程.Python提供了multiprocessing.    multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似.  multiprocessing模块的功能众多:支持子进程.通信和共享数据.执行不同形式的同步,

python学习_day32_并发编程之多进程

一.背景知识 顾名思义,进程即正在执行的一个过程.进程是对正在运行程序的一个抽象.进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所有内容都是围绕进程的概念展开的. PS:即使可以利用的cpu只有一个(早期的计算机确实如此),也能保证支持(伪)并发的能力.将一个单独的cpu变成多个虚拟的cpu(多道技术:时间多路复用和空间多路复用+硬件上支持隔离),没有进程的抽象,现代计算机将不复存在. #一 操作系统的作用: 1:隐藏丑陋复杂的