多进程
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()方法是强制结束工作进程,不再处理未处理的任务,所以对于未处理的任务,就是这样的:
不过要慎用,容易引起queue与pipe的错误
而join()方法则是在等待所有子进程结束.
他们就像是巧克力和雨天,要配合食用才是正确的姿势呢!
②pool.apply_async()方法是非堵塞的,正因为这个特性,所以那个本该在最后的家伙才能跑到最前面去!
(但我换上pool.apply()之后它依旧在前面,此处存疑)
以下为multiprocessing.
Process类所有的方法及属性:
class multiprocessing.
Process
(group=None, target=None, name=None, args=(), 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!')
看一蛤输出:
输出语句果然马上就能看到,我们再来看一个使用wait()方法的栗子:
import subprocess child = subprocess.Popen(['ping','www.qq.com']) child.wait() print('you can see me now!')
输出:
该对象的其他方法:
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大神的文章: