栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Python中的进程

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Python中的进程

Python中的进程
  • 一、前言
  • 二、创建进程的常用方式
    • 1.使用multiprocessing模块创建进程
    • 2.使用Process子类创建进程
    • 3.使用进程池Pool创建进程
  • 三、通过队列实现进程间通信


一、前言

  在了解进程之前,我们需要知道多任务概念。多任务,顾名思义,就是指操作系统能够执行多个任务。例如,使用Windows或Linux操作系统可以同时看电影、聊天、查看网页等,此时,操作系统就是在执行多任务,而每一个任务就是一个进程。我们可以打开Windows的任务管理器,可以查看一下系统正在执行的进程,如图所示:

  进程(process)是计算机中已运行程序的实体。进程和程序不同,程序本身只是指令、数据及其组织形式的描述,进程才是程序(指令和数据)的真正运行实例。例如,在没有打开QQ时,QQ只是程序。打开QQ后,系统就为QQ开启了一个进程。再打开一个QQ,则又开启了一个进程。


二、创建进程的常用方式

  在Python中有多个模块可以创建进程,比较常用的有os.fork()函数、multiprocessing模块和Pool进程池。由于os.fork()函数只适用于Unix/Linux/Mac系统上运行,所以本文重点介绍multiprocessing模块和Pool进程池这两个跨平台模块。


1.使用multiprocessing模块创建进程

  multiprocessing模块提供了一个Process类来代表一个进程对象,语法如下:

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

Process类的参数说明如下:

  • group:参数未使用,值始终为None。
  • target:表示当前进程启动时执行的可调用对象。
  • name:为当前进程实例的别名。
  • agrs:表示传递给target函数的参数元组。
  • kwargs:表示传递给target函数的参数字典。

  例如,实例化Process类,执行子进程,代码如下:

from multiprocessing import Process


# 执行子进程代码
def test(interval):
    print("我是子进程")


# 执行主程序
def main():
    print("主进程开始")
    p = Process(target=test, args=(1,))  # 实例化Process进程类
    p.start()  # 启动子程序
    print("主程序结束")


if __name__ == "__main__":
    main()

  运行结果如下:

  上述代码中,先实例化Process类,然后使用p.start()方法启动子进程,开始执行test()函数。Process的实例p常用的方法除start()外,还有如下常用的方法:

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

  Process类还有如下常用属性:

  • name:当前进程实例别名,默认为Proc-N,N为从1开始递增的整数。
  • pid:当前进程实例的PID值。

  下面通过一个简单示例演示Process类的方法和属性的使用,创建2个子进程,分别使用os模块和time模块输出父进程和子进程的ID以及子进程的时间,并调用Process类的name和pid属性,代码如下:

# _*_ coding:utf-8 _*_
from multiprocessing import Process
import time
import os


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


def child_2(interval):
    print("子进程(%s)开始执行,父进程为(%s)" % (os.getpid(), os.getppid()))
    t_start = time.time()  # 计时开始
    time.sleep(interval)  # 程序将会被挂起interval秒
    t_end = time.time()  # 计时结束
    print("子进程(%s)执行时间为'%0.2f'秒" % (os.getppid(), t_end - t_start))


if __name__ == "__main__":
    print("-----------------父进程开始执行-------------")
    print("父进程PID:%s" % os.getpid())  # 输出当前程序的PID
    p1 = Process(target=child_1, args=(1,))  # 实例化进程p1
    p2 = Process(target=child_2, name='mrsoft', args=(1,))  # 实例化进程p2
    p1.start()  # 启动进程p1
    p2.start()  # 启动进程p2

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

    # 输出p1和p2进程的别名和PID
    print("p1.is_alive = %s" % p1.name)
    print("p1.pid=%s" % p1.pid)
    print("p2.is_alive = %s" % p2.name)
    print("p2.pid=%s" % p2.pid)
    print("----------------等待子进程-------------------")

    p1.join()  # 等待p1进程结束
    p2.join()  # 等待p2进程结束
    print("----------父进程执行结束----------------------")

  上述代码中,第一次实例化Process类时,会为name属性默认赋值为“Process-1”,第二次则默认为“Process-2”,但是由于在实例化进程p2时,设置了name属性为“mrsoft”,所以p2.name的值为“mrsoft”而不是“Process-2”。程序运行流程示意图如图所示:

  运行结果如图所示:


2.使用Process子类创建进程

  对于一些简单的小任务,通常使用Process(target=test)方式实现多进程。但是如果要处理复杂任务的进程,通常定义一个类,使其进程Process类,每次实例化这个类的时候,就等同于实例化一个进程对象。下面,通过一个实例来学习一下如何通过使用Process子类创建多个进程。

  使用Process子类方式创建2个子进程,分别输出父、子进程的PID,已经子进程的运行状态和运行时间,代码如下:

# _*_ coding:utf-8 _*_
from multiprocessing import Process
import time
import os


# 继承Process类
class Subprocess(Process):
    # 由于Process类本身也有__init__初始化方法,这个子类相当于重写了父类的这个方法
    def __init__(self, interval, name=''):
        Process.__init__(self)  # 调用Process父类的初始化方法
        self.interval = interval  # 接收参数interval
        if name:
            self.name = name  # 如果传递参数name,则为子进程创建name属性,否则使用默认属性

    # 重写了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__":
    print("------------父进程开始执行-----------")
    print("父进程PID:%s" % os.getpid())  # 输出当前程序的ID
    p1 = Subprocess(interval=1, name='mrsoft')
    p2 = Subprocess(interval=2)

    # 对一个不包含target属性的Process类执行start()方法,就会运行这个类中的run()方法
    # 所以这里会执行p1.run()
    p1.start()  # 启动p1进程
    p2.start()  # 启动p2进程

    # 输出p1和p2进程的执行状态,如果真正进行,返回True;否则返回False
    print("p1.is_alive=%s" % p1.is_alive())
    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)
    print("----------------等待子进程-------------------")

    p1.join()  # 等待p1进程结束
    p2.join()  # 等待p2进程结束
    print("----------父进程执行结束----------------------")

  上述代码中,定义一个Subprocess子类,继承mulitprocess.Process父类。Subprocess子类中定义了2个方法:__ inti__()初始化方法和run方法。在__ inti__()初始化方法中,调用mulitprocess.Process父类的__ inti__()初始化方法,否则父类初始化方法将被覆盖,无法开启进程。此外在Subprocess子类中并没有定义start()方法,但主进程中却调用了start()方法,此时就会自动执行Subprocess类的run()方法。运行结果如下图所示:


3.使用进程池Pool创建进程

  前面我们使用Process类创建了2个进程。如要要创建几十个或者上百个进程,则需要更多个Process类。有没有更好的创建进程方式解决这类问题呢?答案就是使用mulitprocessing模块提供的Pool类,即Pool进程池。

  为了更好的理解进程池,可以将进程池比作水池,如图所示。我们需要完成放满10个水盆的水的任务,而这个水池中,最多打开3个水龙头开始放水,也就是同时可以执行3个任务,即开启3个进程。为了更快的完成任务,现打开3个水龙头开始放水,当有一个水盆的水接满时,即该进程完成1个任务,我们就将这个水盆的水倒入水桶中,然后继续接水,即执行下一个任务。如果3个水盆同时装满水,那么在放满第9盆水,系统会随机分配1个水盆接水,另外两个水盆闲着。

  接下来,先来了解一下Pool类的常用方法。常用方法及说明如下:

  • apply_async(func[,args[,kwds]]):使用非阻塞方式调用func()函数(并执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递func()函数的参数列表,kwds为传递给func()函数的关键字参数列表。
  • apply(func[,args[,kwds]]):使用阻塞方式调用func()函数。
  • close():关闭Pool,使其不再接受新的任务。
  • terminate():不管任务是否完成,立即终止。
  • join():主进程阻塞,等待子进程的退出,必须在close或terminate之后使用。

  在上面的方法提到了apply_async()使用非阻塞方式调用函数,而apply()使用阻塞方式调用函数。那么什么又是阻塞和非阻塞呢?在下图所示,分别使用阻塞方式和非阻塞方式执行3个任务。如果使用阻塞方式,必须等待上一个进程退出才能执行下一个进程,而使用非阻塞方式,则可以并行执行3个进程。

  下面通过一个示例演示一下如何使用进程池创建多进程。这里模拟水池放水的场景,定义一个进程池,设置最大进程数为3。然后使用非阻塞方式执行10个任务,查看每个进程执行的任务。具体代码如下:

# _*_ coding:utf-8 _*_
from multiprocessing import Pool
import time
import os


def task(name):
    print("子进程(%s)执行task %s ..." % (os.getpid(), name))
    time.sleep(1)  # 休眠1秒


if __name__ == "__main__":
    print("父进程(%s)." % os.getppid())
    p = Pool(3)  # 定义一个进程池,最大进程数为3
    for i in range(10):
        p.apply_async(task, args=(i,))  # 使用非阻塞方式调用函数task()函数
    print("等待所有子进程结束...")
    p.close()
    p.join()
    print("所有子进程结束.")

  运行结果如图所示,从图中可以看出6588的子进程执行了4个任务,而其余2个子进程分别执行了3个任务。


三、通过队列实现进程间通信

  预知详情,请期待下篇,关注 ζ小菜鸡,让我们一起学习,咱们顶峰相见(๑•̀ㅂ•́)و✧

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/961519.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号