天天看点

Python 多进程编程 ThreadPoolExecutor, multiprocessing

一、并发与并行的区别

  1. 并发:一个时间段内,有几个程序在同一个CPU上运行,但是任意时刻只有一个程序在CPU上运行。
  2. 并行:并行是指任意时刻上,有多个程序同时运行在多个CPU上所有常说高并发,没有说高并行,因为CPU数量是有限的。

二、多进程与多线程的区别

  1. 对于耗 CPU 的操作,用多进程优于多线程,因为有 GIL 锁,不会常释放,耗 CPU 操作无法使用多线程。
  2. 多 IO 操作,常会释放 GIL,所以可以多使用多线程。

三、多进程编程

1. 使用 fork()  方法创建子进程(fork只能用于linux/unix中)。

import os

# fork只能用于linux/unix中
pid = os.fork()
print("bobby")
if pid == 0:    # 子进程运行这段
  print('子进程 {} ,父进程是: {}.' .format(os.getpid(), os.getppid()))
else:    # 父进程运行这段
  print('我是父进程:{}.'.format(pid))

"""
在 linux 下运行后,会打印:
bobby
我是父进程: 24474
bobby
子进程 24474, 父进程是:24473
"""
           

fork() 方法创建一个子进程,子进程会将父进程里的数据、代码原样拷贝到子进程当中,父进程依然会向下运行,父子进程都从 fork() 的地方继向下运行。

2. 使用 ProcessPoolExecutor 进行多进程编程。

from concurrent.futures import ProcessPoolExecutor
           

ProcessPoolExecutor 接口跟  ThreadPoolExecutor 的 api 几乎一模一样,具体使用方法可以参照上一篇博客:python线程池 ThreadPoolExecutor 使用详解

3. 使用 multiprocessing 模块进行多进程编程。

(1)multiprocessing.Process

import multiprocessing as mp
threads = []
for i in path:
  threads.append(mp.Process(target=md5sum,args=(i,)))
#创建一个multiprocessing.process.Process对象
#执行
for m in threads:
  m.start()
#回收
for m in threads:
  m.join()
           

(2)使用线程池 multiprocessing.Pool()

def test():
  pass

# 创建一个multiprocessing.pool.Pool的对象
p1 = mp.Pool(processes =5)

#向进程池里添加任务
result = pool.apply_async(test, args=(3,))

# 关闭进程池,阻止更多的任务提交到进程池Pool,待任务完成后,工作进程会退出
p1.close()

# 结束工作进程,不再处理未完成的任务
# p.terminate() 

# 等待工作线程的退出,必须在close()或terminate()之后使用,因被终止的进程需要被父进程调用wait(join等价于wait),否则进程会成为僵尸进程。
p1.join()

print(result.get())
           

(a) Pool里有两种添加子进程的方法:

  • apply_async 异步添加
  • p1.apply(test) 阻塞式添加,逐步执行,相当于单进程

(b) 当Pool所有的进程任务完成后,会产生5个僵尸进程,如果主线程不结束,系统不会自动回收资源,需要调用join函数去回收。

(c) map() 方法:它融合了map函数和apply_async()函数的功能

(d) p.close():关闭进程池,阻止更多的任务提交到进程池Pool,待任务完成后,工作进程会退出

(e) p.terminate():结束工作进程,不再处理未完成的任务

(f) p.join():等待工作线程的退出,必须在close()或terminate()之后使用,因被终止的进程需要被父进程调用wait(join等价于wait),否则进程会成为僵尸进程。

参考文章: [Python]multiprocessing

继续阅读