multiprocessing库:Python像线程一样管理

开发 后端
multiprocessing库是基于threading API,它可以把工作划分为多个进程。有些情况下,multiprocessing可以作为临时替换取代threading来利用多个CPU内核,相应地避免Python全局解释器锁所带来的计算瓶颈。

[[400602]]

前言

multiprocessing库是基于threading API,它可以把工作划分为多个进程。有些情况下,multiprocessing可以作为临时替换取代threading来利用多个CPU内核,相应地避免Python全局解释器锁所带来的计算瓶颈。

下面,我们来看看multiprocessing库创建进程与threading库有多像。

创建一个进程

要创建一个进程,最简单的方式是用一个目标函数实例化一个Process对象,然后与threading一样调用start()函数让它工作。示例如下:

  1. import multiprocessing 
  2.  
  3. def worker(): 
  4.     for i in range(3): 
  5.         print(i) 
  6.  
  7. if __name__=="__main__"
  8.     p = multiprocessing.Process(target=worker) 
  9.     p.start() 

 运行之后,效果如下: 

需要注意的是,multiprocessing库在Windows创建进程必须在if __name__=="__main__":中,这是 Windows 上多进程的实现问题。在 Windows 上,子进程会自动 import 启动它的这个文件,而在 import 的时候是会执行这些语句的。如果直接创建就会无限递归创建子进程报错。所以必须把创建子进程的部分用那个 if 判断保护起来,import 的时候 __name__ 不是 __main__ ,就不会递归运行了。

设置进程名

在threading线程中,我们可以通过其参数name设置线程名,同样的我们也可以通过name参数设置其进程的名字。示例如下:

  1. import multiprocessing 
  2. import time 
  3.  
  4. def worker(): 
  5.     print(multiprocessing.current_process().name"start"
  6.     time.sleep(2) 
  7.     print(multiprocessing.current_process().name"end"
  8.  
  9. if __name__ == "__main__"
  10.     p1 = multiprocessing.Process(name='p1', target=worker) 
  11.     p2 = multiprocessing.Process(name='p2', target=worker) 
  12.     p3 = multiprocessing.Process(name='p3', target=worker) 
  13.     p1.start() 
  14.     p2.start() 
  15.     p3.start() 

 运行之后,效果如下:

守护进程

和线程一样,在所有子进程没有退出之前,主程序是不会退出的。有时候,我们可能需要启动一个后台进程,它可以一直运行而不阻塞主程序退出。

要标志一个守护进程,可以将其添加第3个参数daemon,设置为True。默认值为False,不作为守护进程。示例如下:

  1. import multiprocessing 
  2. import time 
  3.  
  4. def worker(): 
  5.     print(multiprocessing.current_process().name"start"
  6.     time.sleep(1) 
  7.     print(multiprocessing.current_process().name"end"
  8.  
  9. def worker2(): 
  10.     print(multiprocessing.current_process().name"start"
  11.     time.sleep(2) 
  12.     print(multiprocessing.current_process().name"end"
  13.  
  14. if __name__ == "__main__"
  15.     p1 = multiprocessing.Process(name='p1', target=worker) 
  16.     p2 = multiprocessing.Process(name='p2', target=worker2, daemon=True
  17.     p3 = multiprocessing.Process(name='p3', target=worker2, daemon=True
  18.     p1.start() 
  19.     p2.start() 
  20.     p3.start() 

 运行之后,效果如下:

p2,p3为守护进程,但p1不是所以执行1秒之后,就退出主程序了,也就没有打印p2p3的内容。但是其依旧在执行中,直到执行完成。

join()

同样的,如果你期望强制等待一个守护进程的结束,可以增加join()函数。还是上面的代码,示例如下:

  1. import multiprocessing 
  2. import time 
  3.  
  4. def worker(): 
  5.     print(multiprocessing.current_process().name"start"
  6.     time.sleep(1) 
  7.     print(multiprocessing.current_process().name"end"
  8.  
  9. def worker2(): 
  10.     print(multiprocessing.current_process().name"start"
  11.     time.sleep(2) 
  12.     print(multiprocessing.current_process().name"end"
  13.  
  14. if __name__ == "__main__"
  15.     p1 = multiprocessing.Process(name='p1', target=worker) 
  16.     p2 = multiprocessing.Process(name='p2', target=worker2, daemon=True
  17.     p3 = multiprocessing.Process(name='p3', target=worker2, daemon=True
  18.     p1.start() 
  19.     p2.start() 
  20.     p3.start() 
  21.     p1.join() 
  22.     p2.join() 
  23.     p3.join() 

 运行之后,和设置进程名的运行结果一样,这里不再展示。唯一与守护进程代码的区别就是最后三行join()函数代码。当然,也可以像线程一样,给join()函数传入一个时间,超过这个时间,主线程不再等待。

强制结束进程

如果一个进程已经挂起或者不小心进入了死锁状态,那么这个时候,我们往往会强制的结束进程。对一个进程对象调用terminate()会结束子进程。示例如下:

  1. import multiprocessing 
  2. import time 
  3.  
  4. def worker(): 
  5.     print(multiprocessing.current_process().name"start"
  6.     time.sleep(5) 
  7.     print(multiprocessing.current_process().name"end"
  8.  
  9. if __name__ == "__main__"
  10.     p1 = multiprocessing.Process(name='p1', target=worker) 
  11.     p1.start() 
  12.     print("是否还在运行", p1.is_alive()) 
  13.     p1.terminate() 
  14.     print("是否还在运行", p1.is_alive()) 
  15.     p1.join() 
  16.     print("是否还在运行", p1.is_alive()) 

 运行之后,输出如下:

终止进程后要使用join()函数等待进程的退出。使进程管理代码有足够的时间更新对象的状态,以反应进程已经终止。

进程退出状态码

进程退出时,生成的状态码可以通过exitcode属性访问。下表就是其状态码的取值范围以及其意义:

测试如下:

  1. import multiprocessing 
  2. import time 
  3.  
  4. def worker(): 
  5.     print(multiprocessing.current_process().name"start"
  6.     time.sleep(5) 
  7.     print(multiprocessing.current_process().name"end"
  8.  
  9. if __name__ == "__main__"
  10.     p1 = multiprocessing.Process(name='p1', target=worker) 
  11.     p2 = multiprocessing.Process(name='p2', target=worker) 
  12.     p1.start() 
  13.     p2.start() 
  14.     print("是否还在运行", p1.is_alive()) 
  15.     p1.terminate() 
  16.     print("是否还在运行", p1.is_alive()) 
  17.     print(p1.exitcode) 
  18.     p1.join() 
  19.     print("是否还在运行", p1.is_alive()) 
  20.     print(p1.exitcode) 
  21.     time.sleep(5.5) 
  22.     print(p2.exitcode) 

 运行之后,效果如下:

可以看到,强制退出的进程错误码为负数,正常退出的进程错误码为0。

日志

调试并发问题时,如果能够访问multiprocessing所提供对象的内部状态,那么这会很有用。在实际的项目中,我们可以使用一个方便的模块级函数启用日志记录,它使用logging建立一个日志记录器对象,并增加一个处理器,使日志消息被发送到标准错误通道。

示例如下:

  1. import multiprocessing 
  2. import logging 
  3. import sys 
  4.  
  5. def worker(): 
  6.     print("运行工作进程"
  7.     sys.stdout.flush() 
  8.  
  9. if __name__ == "__main__"
  10.     multiprocessing.log_to_stderr(logging.DEBUG) 
  11.     p1 = multiprocessing.Process(name='p1', target=worker) 
  12.     p1.start() 
  13.     p1.join() 

 运行之后,效果如下:

派生进程

与线程一样,我们可以自定义进程,而不必只是传入一个函数进行进程的创建。

创建的进程的方式也是派生自进程类即可。示例如下:

  1. import multiprocessing 
  2.  
  3. class WorkerProcess(multiprocessing.Process): 
  4.     def run(self): 
  5.         print(self.name
  6.         return 
  7.  
  8. if __name__ == "__main__"
  9.     for i in range(5): 
  10.         p = WorkerProcess() 
  11.         p.start() 
  12.         p.join() 

 运行之后,效果如下:

multiprocessing库的进程知识与threading一样长,因为本篇的内容已经够长了,剩下的知识我们将在下一篇博文中接着讲解。

 

责任编辑:姜华 来源: 今日头条
相关推荐

2013-12-31 09:19:23

Python调试

2013-12-17 09:02:03

Python调试

2023-05-23 13:59:41

RustPython程序

2023-04-05 14:19:07

FlinkRedisNoSQL

2017-05-22 10:33:14

PythonJuliaCython

2022-12-21 15:56:23

代码文档工具

2012-03-21 21:14:33

乔布斯

2020-08-25 08:56:55

Pythonawk字符串

2014-09-22 09:27:57

Python

2011-01-18 10:45:16

乔布斯

2012-06-08 13:47:32

Wndows 8Vista

2015-02-05 13:27:02

移动开发模块SDK

2013-08-22 10:17:51

Google大数据业务价值

2015-03-16 12:50:44

2022-08-03 08:52:35

Bytebase开源

2011-10-24 13:07:00

2015-04-09 11:27:34

2012-06-14 09:48:11

OpenStackLinux

2021-09-07 10:29:11

JavaScript模块CSS

2012-03-21 10:15:48

RIM越狱
点赞
收藏

51CTO技术栈公众号