多进程(mutiprocessing)与多线程(Threading)之多进程

时间:2021-09-12 16:43:17

多进程

1.multiprocessing下的Process类表示一个进程对象

 1 from multiprocessing import Process
 2 import os
 3 
 4 def f1(arg):
 5     print('I am a process,my name is %s and I am running!'%arg)
 6 
 7 if __name__ == '__main__':
 8     print('I am the parent process of the following process!my id is %s'%os.getpid())
 9     '''
10     创建一个进程对象p,target参数应传入函数名,arg要求是一个tuple,用于传入函数的参数,其后还有可选参数**kyargs
11     '''
12     p = Process(target=f1,args=('child',))
13     #开始进程
14     p.start()
15     #主进程等待子进程结束
16     p.join()
17     print('process is end.')

output:

I am the parent process of the following process!my id is 5760
I am a process,my name is child and I am running!
process is end.

join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步

2.multiprocess下的Pool类可用于批量创建子进程

import multiprocessing
import time

def zhangzhe(arg):
    print('我是%s号长者,请为我续命!'%arg)
    #-3s
    time.sleep(3)


if __name__ == '__main__':
    #processes参数创建的进程数,默认为电脑最大CPU核心数
    pool = multiprocessing.Pool(processes=4)
    for i in range(5):
        arg = i
        pool.apply_async(zhangzhe,(i,))
    print('我是最后,但我在最前,蛤蛤蛤!')
    pool.close()
    pool.join()
    print('程序结束!')

output:

我是最后,但我在最前,蛤蛤蛤!
我是1号长者,请为我续命!
我是2号长者,请为我续命!
我是3号长者,请为我续命!
我是0号长者,请为我续命!
我是4号长者,请为我续命!
程序结束!

要注意的是:

①在调用pool.join()方法前需调用pool.close()方法或者terminate()方法,否则会出错!

pool.close()方法后不会有心的进程加入到pool,

而terminate()方法是强制结束工作进程,不再处理未处理的任务,所以对于未处理的任务,就是这样的:

多进程(mutiprocessing)与多线程(Threading)之多进程

不过要慎用,容易引起queue与pipe的错误

而join()方法则是在等待所有子进程结束.

他们就像是巧克力和雨天,要配合食用才是正确的姿势呢!

②pool.apply_async()方法是非堵塞的,正因为这个特性,所以那个本该在最后的家伙才能跑到最前面去!

(但我换上pool.apply()之后它依旧在前面,此处存疑)

 

以下为multiprocessing.Process类所有的方法及属性:

class multiprocessing.Process(group=Nonetarget=Nonename=Noneargs=()kwargs={}*daemon=None)

run()
表示过程活动的方法。您可以在子类中覆盖此方法。标准的run()方法调用传递给对象的构
造函数的可调用对象作为目标参数(如果有的话),其顺序和关键字参数分别从args和
kwargs参数中取出。
start()

启动流程的活动。每个进程对象最多只能调用一次。它安排对象的run()方法在单
独的进程中被调用。
join([timeout])

如果可选参数timeout为None(默认值),该方法将阻塞,直到调用join(
)方法的进程终止。如果超时为正数,则阻塞至多超时秒。请注意,如果方法终止或方法超
时,该方法返回None。检查进程的exitcode以确定是否终止。一个进程可以连接很多次。进
程无法连接,因为这将导致死锁。尝试在进程开始之前加入进程是一个错误。
name
进程的名称。该名称是用于识别目的的字符串。它没有语义多个进程可以被赋予相同的名称
。初始名称由构造函数设置。如果没有向构造函数提供明确的名称,则构造“Process-N1:
N2:...:Nk”形式的名称,其中每个Nk是其父级的第N个子节点。
is_alive()

返回过程是否活着。
从start()方法返回直到子进程终止为止,一个进程对象就活着。
daemon
进程的守护进程标志,一个布尔值。
这必须在start()被调用之前进行设置。初始值从创建过程继承。当进程退出时,它会尝试
终止所有的守护进程子进程。请注意,守护进程不允许创建子进程。否则,如果在其父进程
退出时终止,则守护进程将使其子进程成为孤岛。此外,这些不是Unix守护程序或服务,它
们是正常进程,如果非守护进程已退出,则它们将被终止(并且未加入)。

除了 threading.Thread API之外,Process对象还支持以下属性和方法:

PID
返回进程ID。在进程生成之前,这将是无。
exitcode
孩子的退出代码。如果进程尚未终止,则为“否”。负值-N表示孩子被信号N终止。
AUTHKEY
进程的认证密钥(一个字节字符串)。当初始化多处理器时,使用os.urandom()为主进程
分配一个随机字符串。当一个Process对象创建时,它将继承其父进程的验证密钥,尽管这可
以通过将authkey设置为另一个字节字符串来更改。请参阅验证密钥。
sentinel
对象的数字句柄将在进程结束时变为“准备好”。如果要使用
multiprocessing.connection.wait()等待几个事件,可以使用此值。否则调用join()更简单
。在Windows上,这是一个可用于WaitForSingleObject和WaitForMultipleObjects API调用系
列的操作系统句柄。在Unix上,这是一个可以从select模块中使用原语的文件描述符。版本
3.3中的新功能。
terminate()
终止进程。在Unix上,这是使用SIGTERM信号完成的;在Windows上使用TerminateProcess(
)。注意,退出处理程序和最后的子句等将不被执行。请注意,进程的后代进程将不会被终
止 - 它们将变得孤立。

注意!守护进程的三个特点:父进程结束时自动结束;不能产生新的子进程;必须在start()方法前设定!

 

子进程(Subprocess库)

subprocess库可以帮助我们创建子进程,执行外部程序(本质是通过开始-运行-cmd(Windows下)或shell(Linux下)输入命令运行)

使用该库创建子进程要注意三点:

①在创建子进程之后,父进程是否暂停等待子进程运行

②函数返回了什么  

③当return不为0时,父进程如何处理

以下是一些常用的方法函数:

subprocess.call()

父进程等待子进程完成

返回退出信息(returncode)

subprocess.check_call()

父进程等待子进程完成,返回0,检查退出信息,正常为0,否则抛出subprocess.CallProcessError

该对象包含有returncode属性,可用try...except...来检查

subprocess.check_output()

父进程等待子进程完成,返回子进程向标准输出的输出结果

检查退出信息,正常为0,否则抛出subprocess.CallProcessError

该对象包含有returncode属性和output属性,output属性为标准输出的输出结果,可用try...except...来检查

#一个简单的栗子
import
subprocess out = subprocess.call('ping','www.qq.com','-t')
#就相当于开始-运行-输入 ping www.qq.com -t

 

Popen()类

 

Popen()类可帮助我们定制子进程,实例化该类即可视为一个子进程

但open()实例化后,主程序不会自动等待子进程完成,我们要手动调用对象的wait()方法

#一个简单的例子
import
subprocess child = subprocess.Popen(['ping','www.qq.com']) print('you can see me immediately!')

看一蛤输出:

多进程(mutiprocessing)与多线程(Threading)之多进程

输出语句果然马上就能看到,我们再来看一个使用wait()方法的栗子:

import subprocess
child = subprocess.Popen(['ping','www.qq.com'])
child.wait()
print('you can see me now!')

输出:

多进程(mutiprocessing)与多线程(Threading)之多进程

该对象的其他方法:

child.poll()           # 检查子进程状态

child.kill()           # 终止子进程

child.send_signal()    # 向子进程发送信号

child.terminate()      # 终止子进程

child.pid  #获取子进程ID

子进程的文本流控制:

child.stdin  #标准输入

child.stdout  #标准输出

child.stderr  #标准错误

创建子进程是,需要明确该子进程的是否有输入输出和错误,分别来自哪里,有的话应该在Popen()里的可选参数中设定

我们可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,

并可以利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe):

import subprocess
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE)
out = child2.communicate()
print(out)

subprocess.PIPE实际上为文本流提供一个缓存区。child1的stdout将文本输出到缓存区,随后child2的stdin从该PIPE中将文本读取走。child2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。

要注意的是,communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成。

我们还可以利用communicate()方法来使用PIPE给子进程输入:

import subprocess
child = subprocess.Popen(["cat"], stdin=subprocess.PIPE)
child.communicate("something")

我们启动子进程之后,cat会等待输入,直到我们用communicate()输入"something"。

 

注:文章部分参考了Vamei大神的文章:

Python标准库06 子进程 (subprocess包)