欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

浅谈Python多进程中的multiprocessing

程序员文章站 2022-06-16 08:18:32
前言:一、多任务的引入在现实生活中,有很多的场景中的事情是同时进行的,比如开车的时候,手和脚共同来驾驶汽车;再比如,唱歌跳舞也是同时进行的。下来,我们在程序里面,模拟一下“唱歌跳舞”这件事情,如下:from time import sleepdef sing(): for i in range(3): print("正在唱歌...%d"%i) sleep(1) def dance(): for i in range(3):...

前言:本博文主要Python多进程中的multiprocessing

友情链接:

  1. 浅谈Python多进程中的fork
  2. 浅谈Python并发编程之进程(守护进程、锁、信号量)

一、多任务的引入

在现实生活中,有很多的场景中的事情是同时进行的,比如开车的时候,手和脚共同来驾驶汽车;再比如,唱歌跳舞也是同时进行的。

下来,我们在程序里面,模拟一下“唱歌跳舞”这件事情,如下:

from time import sleep


def sing():
    for i in range(3):
        print("正在唱歌...%d"%i)
        sleep(1)

        
def dance():
    for i in range(3):
        print("正在跳舞...%d"%i)
        sleep(1)

        
if __name__ == '__main__':
    sing() #唱歌
    dance() #跳舞

浅谈Python多进程中的multiprocessing

注意:

  1. 很显然刚刚的程序并没有完成唱歌和跳舞同时进行的要求。
  2. 如果想要实现“唱歌跳舞”同时进行,那么就需要一个新的方法,叫做:多任务。

二、多任务的概念

什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,我们一边在用浏览器上网,一边在听MP3,一边在用Word写作业,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。

现在,多核CPU已经非常普及了。但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢?

答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。

真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。

实现多任务的策略(调度算法):

  1. 时间片轮转
  2. 优先级调度

实现多任务的三种方式:

  1. 进程
  2. 线程
  3. 协程

多任务的原理:

  1. 并发:假的多任务,时间片的轮转,快速的交替运行任务。
  2. 并行:真的多任务,一个核处理一个任务。

三、进程和程序的区别

编写完毕的代码,在没有运行的时候,称之为程序。正在运行着的代码,就成为进程。

进程,除了包含代码以外,还有需要运行的环境等,所以和程序是有区别的。

四、multiprocessing

如果你打算编写多进程的服务程序,Unix/Linux无疑是正确的选择。由于Windows没有fork调用,难道在Windows上无法用Python编写多进程的程序?

由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。

multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:

from multiprocessing import Process
import os


# 子进程要执行的代码
def run_proc(name):
    print('子进程运行中,name= %s ,pid=%d...' % (name, os.getpid()))

    
if __name__ == '__main__':
    print('父进程 %d.' % os.getppid())
    p = Process(target=run_proc, args=('test',))
    print('子进程将要执行')
    p.start()
    p.join()
    print('子进程已结束')

浅谈Python多进程中的multiprocessing

说明:

  1. 创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。
  2. join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。

五、Process语法结构

Process([group [, target [, name [, args [, kwargs]]]]])
  • target:表示这个进程实例所调用对象;
  • args:表示调用对象的位置参数元组;
  • kwargs:表示调用对象的关键字参数字典;
  • name:为当前进程实例的别名;
  • group:大多数情况下用不到;

Process类常用方法:

  • is_alive():判断进程实例是否还在执行;
  • join([timeout]):是否等待进程实例执行结束,或等待多少秒;
  • start():启动进程实例(创建子进程);
  • run():如果没有给定target参数,对这个对象调用start()方法时,就将执行对象中的run()方法;
  • terminate():不管任务是否完成,立即终止;

Process类常用属性:

  • name:当前进程实例别名,默认为Process-N,N为从1开始递增的整数;
  • pid:当前进程实例的PID值;
from multiprocessing import Process
import os
from time import sleep

# 子进程要执行的代码
def run_proc(name, age, **kwargs):
    for i in range(10):
        print('子进程运行中,name= %s,age=%d ,pid=%d...' % (name, age,os.getpid()))
        print(kwargs)
        sleep(0.5)

        
if __name__ == '__main__':
    print('父进程 %d.' % os.getppid())
    p = Process(target=run_proc, args=('test',18), kwargs={"m":20})
    print('子进程将要执行')
    p.start()
    sleep(1)
    p.terminate()
    p.join()
    print('子进程已结束')

浅谈Python多进程中的multiprocessing

进程编号的作用:

  • 进程的编号的目的是验证主进程和子进程的关系,可以得知子进程是由哪个主进程创建出来的。

获取进程编号的方式:

  1. 获取主(父)进程pid:os.getppid()
  2. 获取子进程pid:os.getpid()
  • 所有的子进程都来自于父进程,因此一个程序中的主进程编号得知,子进程编号按照主进程编号为起始值加一计算。
  • multiprocessing.current_process()方法获取当前的当前进程的详细信息(进程名称和进程编号)。

multiprocessing.current_process()方法获取当前的当前进程的详细信息(进程名称和进程编号)

from multiprocessing import Process
import time
import os

#两个子进程将会调用的两个方法
def  worker_1(interval):
    print("worker_1,父进程(%s),当前进程(%s)"%(os.getppid(),os.getpid()))
    t_start = time.time()
    time.sleep(interval) #程序将会被挂起interval秒
    t_end = time.time()
    print("worker_1,执行时间为'%0.2f'秒"%(t_end - t_start))


def  worker_2(interval):
    print("worker_2,父进程(%s),当前进程(%s)"%(os.getppid(),os.getpid()))
    t_start = time.time()
    time.sleep(interval)
    t_end = time.time()
    print("worker_2,执行时间为'%0.2f'秒"%(t_end - t_start))

#输出当前程序的ID
print("进程ID:%s"%os.getpid())

#创建两个进程对象,target指向这个进程对象要执行的对象名称,
#args后面的元组中,是要传递给worker_1方法的参数,
#因为worker_1方法就一个interval参数,这里传递一个整数2给它,
#如果不指定name参数,默认的进程对象名称为Process-N,N为一个递增的整数
p1=Process(target=worker_1,args=(2,))
p2=Process(target=worker_2,name="dongGe",args=(1,))

#使用"进程对象名称.start()"来创建并执行一个子进程,
#这两个进程对象在start后,就会分别去执行worker_1和worker_2方法中的内容
p1.start()
p2.start()

#同时父进程仍然往下执行,如果p2进程还在执行,将会返回True
print("p2.is_alive=%s"%p2.is_alive())

#输出p1和p2进程的别名和pid
print("p1.name=%s"%p1.name)
print("p1.pid=%s"%p1.pid)
print("p2.name=%s"%p2.name)
print("p2.pid=%s"%p2.pid)

#join括号中不携带参数,表示父进程在这个位置要等待p1进程执行完成后,
#再继续执行下面的语句,一般用于进程间的数据同步,如果不写这一句,
#下面的is_alive判断将会是True,在shell(cmd)里面调用这个程序时
#可以完整的看到这个过程,大家可以尝试着将下面的这条语句改成p1.join(1),
#因为p2需要2秒以上才可能执行完成,父进程等待1秒很可能不能让p1完全执行完成,
#所以下面的print会输出True,即p1仍然在执行
p1.join()
print("p1.is_alive=%s"%p1.is_alive())

浅谈Python多进程中的multiprocessing

六、进程的创建-Process子类

创建新的进程还能够使用类的方式,可以自定义一个类,继承Process类,每次实例化这个类的时候,就等同于实例化一个进程对象,请看下面的实例:

from multiprocessing import Process
import time
import os

#继承Process类
class Process_Class(Process):
    #因为Process类本身也有__init__方法,这个子类相当于重写了这个方法,
    #但这样就会带来一个问题,我们并没有完全的初始化一个Process类,所以就不能使用从这个类继承的一些方法和属性,
    #最好的方法就是将继承类本身传递给Process.__init__方法,完成这些初始化操作
    def __init__(self,interval):
        Process.__init__(self)
        self.interval = interval

    #重写了Process类的run()方法
    def run(self):
        print("子进程(%s) 开始执行,父进程为(%s)"%(os.getpid(),os.getppid()))
        t_start = time.time()
        time.sleep(self.interval)
        t_stop = time.time()
        print("(%s)执行结束,耗时%0.2f秒"%(os.getpid(),t_stop-t_start))

if __name__=="__main__":
    t_start = time.time()
    print("当前程序进程(%s)"%os.getpid())
    p1 = Process_Class(2)
    #对一个不包含target属性的Process类执行start()方法,就会运行这个类中的run()方法,所以这里会执行p1.run()
    p1.start()
    p1.join()
    t_stop = time.time()
    print("(%s)执行结束,耗时%0.2f"%(os.getpid(),t_stop-t_start))

浅谈Python多进程中的multiprocessing

七、Process中kill的方法

在python语言中,我们可以使用os模块中的kill方法,根据pid杀死相应进程。

语法:os.kill(进程编号, 信号编号)
浅谈Python多进程中的multiprocessing

八、进程的特性

进程的特性介绍:

  1. 进程之间不共享全局变量
  2. 主进程会等待所有的子进程结束之后再结束

8.1 不共享全局变量

当一个进程对全局变量进行数据的修改,对于其他进程而言不会造成任何的影响,可以理解为每个进程都拿的是最初始的全局变量。或者可以理解为全局变量就是所谓资源,当创建一个进程,则系统会直接给这个进程里面复制一个全局变量。针对于这个全局变量而言,在进程之间都是相互独立存在的,之间没有任何的联系。
浅谈Python多进程中的multiprocessing
三个进程分别操作的都是自己进程内部的全局变量test_list,不会对其他的进程里面的全局变量造成影响,所以进程之间不共享全局变量。他们的关系只有一点,不同进程之间的全局变量的名字相同而已。

8.2 所有子进程结束主进程才会结束

主进程会等待所有的子进程执行结束之后才能结束

在主进程结束之前,手动结束了所有的子进程,那么程序的结束由主进程的结束来控制

如果需要实现主进程结束则整个程序结束:

  1. 在主进程结束之前,保证所有子进程结束使用子进程的terminate()
  2. 在子进程开启之前,设置当前子进程被被主进程守护,子进程的deamon属性设为true则意味着这个子进程被主进程守护,主进程结束守护结束,子进程也结束

九、单进程与多线程的优劣

9.1 单进程

默认程序运行创建一个进程。

一个Python文件运行,就是开启一个进程去处理。

进程中的场景:主线程去执行代码。

9.2 多进程

一个Python文件运行,占用一个进程去处理,假如同时要运行第二个Python文件,同样给第二个Python文件开启一个进程去处理。

多进程可以完成多任务,每个进程就好比一个独立车间,每个车间都各自在运营,每个进程也是各自在运行,执行各自的任务。

9.3 优劣对比

  1. 单进程开发简单;多线程开发复杂;
  2. 单进程在处理高并发时一般采用多启动进程的方式;多线程仅需启动多个线程。进程的切换开销比线程大;
  3. 多进程之间如果有信息通信则相对多线程效率较低,因为多线程属于同一地址空间的访问,效率相对较高(暂不涉及锁等一致性策略的影响);
  4. 多进程稳定好,一个进程死了不影响其他进程;多线程中,任意一个出现问题,将影响到所有。

本文地址:https://blog.csdn.net/qq_44034384/article/details/107885692

相关标签: Python