当前位置 : 主页 > 编程语言 > python >

Python中的进程

来源:互联网 收集:自由互联 发布时间:2022-06-15
Python中的进程 ​​一、前言​​ ​​二、创建进程的常用方式​​ ​​1.使用multiprocessing模块创建进程​​ ​​2.使用Process子类创建进程​​ ​​3.使用进程池Pool创建进程​​ ​​


Python中的进程

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

一、前言

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

  运行结果如下:
Python中的进程_子进程_02

  上述代码中,先实例化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”。程序运行流程示意图如图所示:
Python中的进程_python_03

  运行结果如图所示:
Python中的进程_linux_04


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()方法。运行结果如下图所示:
Python中的进程_python_05


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

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

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

  接下来,先来了解一下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个进程。
Python中的进程_开发语言_07

  下面通过一个示例演示一下如何使用进程池创建多进程。这里模拟水池放水的场景,定义一个进程池,设置最大进程数为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个任务。
Python中的进程_子进程_08


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

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


上一篇:Python3教程:pathlib 模块的用法
下一篇:没有了
网友评论