用到的是multiprocessing模块,from multiprocessing import Process
,Python Process类常用属性和方法,更多方法参考官方文档的参考部分:
属性名或方法名 | 功能 |
---|---|
start() | 和启动子线程一样,新创建的进程也需要手动启动,该方法的功能就是启动新创建的线程。 |
run() | 第 2 种创建进程的方式需要用到,继承类中需要对方法进行重写,该方法中包含的是新进程要执行的代码。 |
join([timeout]) | 和 thread 类 join() 方法的用法类似,其功能是在多进程执行过程,其他进程必须等到调用 join() 方法的进程执行完毕(或者执行规定的 timeout 时间)后,才能继续执行; |
is_alive() | 判断某进程是否存活,存活返回True,否则False。 |
terminate() | 中断该进程。 |
name | 可以为该进程重命名,也可以获得该进程的名称。 |
daemon | 和守护线程类似,通过设置该属性为 True,可将新建进程设置为“守护进程”。 |
pid | 返回进程的 ID 号。大多数操作系统都会为每个进程配备唯一的 ID 号。 |
exitcode | 进程运行时为None,如果为-N,表示被信号N结束了。 |
authkey | 进程身份验证,默认是由os.urandom()随机生成32字符的字符串。这个键的用途是设计涉及网络连接的底层进程间的通信提供安全性,这类连接只有在具有相同身份验证才能成功。 |
如果在windows系统上运行多进程,请把Process放在if __name__ == '__main__':
下。Python程序入口 name == ‘main’ 有重要功能(多线程)而非编程习惯
(以上表格和以下代码参考Python Process创建进程(2种方法)详解,部分进行了修改)
from multiprocessing import Process
import os
# 定义为进程方法传入的参数
my_tuple = ("http://c.biancheng.net/python/",
"http://c.biancheng.net/shell/",
"http://c.biancheng.net/java/")
# 定义一个函数,准备作为新进程的 target 参数,打印my_tuple中的信息
def action(name, *add):
print("I am child, the pid is %d." % os.getpid())
for arc in add:
print("%s --当前进程%d" % (arc, os.getpid()))
if __name__ == '__main__':
print("I am father, the pid is %d." % os.getpid())
# 创建子进程,执行 action() 函数
my_process = Process(target=action, args=("my_process进程", *my_tuple))
# 启动子进程
my_process.start()
my_process.join()
print("I am father, over")
程序执行结果为:
I am father, the pid is 18968.
I am child, the pid is 18984.
http://c.biancheng.net/python/ --当前进程18984
http://c.biancheng.net/shell/ --当前进程18984
http://c.biancheng.net/java/ --当前进程18984
I am father, over
通过 multiprocessing.Process 来创建并启动进程时,程序必须先判断
if __name__=='__main__':
,否则运行该程序会引发异常。
这种运行方式与python中运行线程非常类似,在父进程中Process类的对象,然后产生两个分支,一个分支运行函数action中的程序,一个分支继续运行,最后会和。
通过继承 Process 类的子类,创建实例对象,也可以创建新的进程。注意,继承 Process 类的子类需重写父类的 run() 方法。
from multiprocessing import Process
import os
# 定义为进程方法传入的参数
my_tuple = ("http://c.biancheng.net/python/",
"http://c.biancheng.net/shell/",
"http://c.biancheng.net/java/")
# 自定义一个进程类,从Process继承
class My_Process(Process):
def __init__(self, name, *add):
super().__init__()
self.name = name
self.add = add
def run(self):
print(self.name)
for arc in self.add:
print("%s --当前进程%d" % (arc,os.getpid()))
if __name__ == '__main__':
# 定义为进程方法传入的参数
print("I am father, the pid is %d." % os.getpid())
my_process = My_Process("my_process进程", *my_tuple)
# 启动子进程
my_process.start() # 运行的就是My_Process中run方法中的内容
my_process.join()
print("I am father, over")
程序执行结果为:
I am father, the pid is 1936.
my_process进程
http://c.biancheng.net/python/ --当前进程23288
http://c.biancheng.net/shell/ --当前进程23288
http://c.biancheng.net/java/ --当前进程23288
I am father, over
除了进程id号不同外,其余都相同。
以下代码摘录自一篇文章搞定Python多进程(全)
from multiprocessing import Process
def fun1(idx):
print('测试%d多进程' % idx)
if __name__ == '__main__':
process_list = [Process(target=fun1, args=(x,)) for x in range(0, 5)]
for p in process_list:
p.start()
for p in process_list:
p.join()
print('结束测试')
程序执行结果为:
测试0多进程
测试1多进程
测试2多进程
测试3多进程
测试4多进程
结束测试
Process finished with exit code 0
上面的代码开启了5个子进程去执行函数,将每个进程加入到list中,然后遍历list进行回收。
在C语言多进程编程中,对于进程的回收有严格的要求,一定要把进程回收,不然就变成了僵尸进程占满系统内存,因此有功能齐全的进程回收函数wait()和waitpid()。在python中目前遇到的回收还没有遇到非常好的内容,先挖一个坑。
from multiprocessing import Process
import time
def first():
print("There is no problem here")
def second():
raise RuntimeError("Error raised!")
def third():
time.sleep(3)
print("This process will be terminated")
if __name__ == '__main__':
workers = [Process(target=first), Process(target=second), Process(target=third)]
for w in workers:
w.start()
workers[-1].terminate()
for w in workers:
w.join()
for w in workers:
print(w.exitcode)
程序执行结果为:
There is no problem here
Process Process-2:
Traceback (most recent call last):
File "D:\Anaconda\envs\yolo_pytorch38\lib\multiprocessing\process.py", line 315, in _bootstrap
self.run()
File "D:\Anaconda\envs\yolo_pytorch38\lib\multiprocessing\process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "E:\Code\Yolo_related_code\yolov5-master-main\scripts\processes.py", line 10, in second
raise RuntimeError("Error raised!")
RuntimeError: Error raised!
0
1
-15
当进程结束(或中断)的时候,会产生一个退出码(exitcode),它是一个数字,表示执行的结果。不同的数字分别表示进程正常完结,异常完结,或是由另一个进程中断的状态。
具体有以下三种情况:
等于0表示正常完结
大于0表示异常完结
小于0表示进程被另一个进程通过-1*exit_code信号终结
有时候创建进程并不嫩加快速度,如果原程序需要导入的库很多很大,由于进程创建的特性,速度反而会变慢。
Python多进程可以选择两种创建进程的方式,spawn 与 fork。分支创建:fork会直接复制一份自己给子进程运行,并把自己所有资源的handle 都让子进程继承,因而创建速度很快,但更占用内存资源。分产创建:spawn只会把必要的资源的handle 交给子进程,因此创建速度稍慢。详细解释请看 Stack OverFlow multiprocessing fork vs spawn 。
记一次性能优化的心酸历程【Flask+Gunicorn+pytorch+多进程+线程池,一顿操作猛如虎】
Queue类是一个既线程安全又进程安全的先进先出(FIFO,first in first
out,数据交换机制。
from multiprocessing import Process, Queue
def f(q):
q.put([42, None, 'hello'])
if __name__ == '__main__':
q = Queue()
p = Process(target=f, args=(q,))
p.start()
print(q.get()) # prints "[42, None, 'hello']"
p.join()
一篇文章搞定Python多进程(全)
Python Process创建进程(2种方法)详解
强推这个,这个博主了解的很透彻在Python中优雅地用多进程
官方文档:multiprocessing — 基于进程的并行