> For the complete documentation index, see [llms.txt](https://kerasnoone.gitbook.io/garnet/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kerasnoone.gitbook.io/garnet/gong-cheng-zhan/python/bing-fa/duo-jin-cheng/0x02-jin-cheng-chi-xiang-guan.md).

# 0x02 进程池相关

### 进程池

进程池创建时, 会克隆出一个**Python解释器**, 在`Linux`上还会克隆原解释器中所有的程序状态, 在`Windows`中不会包含运行状态.

另外, 如果将多线程和多进程结合在一起使用, 需要在创建任何线程之前, 优先创建并加载进程池.

有两种方法创建进程池.

#### multiprocessing.Pool

进程池的使用方法如下:

```python
import time
import multiprocessing

def func(msg):
    print("msg:", msg)
    time.sleep(3)
    print("end")

pool = multiprocessing.Pool(processes=3)
for i in range(4):
    msg = "hello %d" %(i)
    pool.apply_async(func, (msg, ))

pool.close()
pool.join()
print("Sub-process(es) done.")
```

**注意**:

* 使用**apply\_async**或**apply**方法向子进程中添加任务, 使用这两种方法添加任务之后, 会立即在子进程中启动运行. 两者的区别在于前者是非阻塞的, 后者是阻塞的, 即使用`apply`方法添加后, 需要等待这个任务执行完毕, 主进程才能继续执行.

  上述代码的执行结果为:

  ```python
  msg: hello 0
  msg: hello 1
  msg: hello 2
  end
  msg: hello 3
  end
  end
  end
  Sub-process(es) done.
  ```

  如果上述代码的`apply_async`方法改为`apply`方法, 执行的结果就是:

  ```python
  msg: hello 0
  end
  msg: hello 1
  end
  msg: hello 2
  end
  msg: hello 3
  end
  Sub-process(es) done.
  ```
* **close**方法的作用是关闭进程池, 进程池**不再接受新的任务**, **join**方法的作用是阻塞父进程. 因此在调用阻塞方法时, **需要先调用进程池的close方法**, 然后再调用`join`方法进行阻塞.
* **terminate()**&#x65B9;法才是真正的关闭进程池的方法, 结束所有工作子进程, 不再处理未完成的任务.
* 为了拿到任务的执行结果, 可以将`apply_async`方法的返回结果进行收集:

  \`\`\`python

  **coding: utf-8**

  import time import multiprocessing

def func(msg): print("msg:", msg) time.sleep(3) print("end")

if **name** == "**main**": pool = multiprocessing.Pool(processes=3) results = \[] for i in range(4): msg = "hello %d" % (i,) results.append(pool.apply\_async(func, (msg,)))

```
  print(type(results[0]))
  pool.close()
  pool.join()
```

```
  输出为:
```

&#x20;msg: hello 0 msg: hello 1 msg: hello 2 end msg: hello 3 end end end

\`\`\`

* 将`apply_async`返回的对象收集起来, 如上面代码使用列表来收集
* `apply_async`返回的对象是`multiprocessing.pool.ApplyResult`对象, 使用该对象的`get()`方法得到任务函数返回的结果, 如果进程还没有执行完, `get()`方法将会产生阻塞.

#### concurrent.futures.ProcessPoolExecutor

如同`ThreadPoolExecutor`的使用方法, 用`submit`或者`map`方法提交任务.

### 两者的区别

在[ProcessPoolExecutor from concurrent.futures way slower than multiprocessing.Pool](https://stackoverflow.com/questions/18671528/processpoolexecutor-from-concurrent-futures-way-slower-than-multiprocessing-pool)中有一个关于两者之间速度的问题. 在处理较多任务的时候, `ProcessPoolExecutor`方法会慢很多, 这主要是因为:

`ProcessPoolExecutor`对于每一个提交的任务, 都会创建一个`Future`对象, 里面会装在很多内容, 如最后运行的结果. 但`multiprocessing.Pool`就不会有这一步, 如果使用它的`map`方法进行提交, `Pool`对象会**批量**地提交任务, 然后直接就把结果返回, 不会产生额外的开销.

因此:

* 在**大批量任务**的应用场景下, 使用`multiprocessing.Pool`进行多进程是更好的选择.
* 当任务执行的时间较长时, 使用`ProcessPoolExecutor`带来的`Futures`更好.
* 如果需要在处理完毕后执行`callback`, 就只能使用`ProcessPoolExecutor`了, 因为`Pool`没有`callback`的方法, 只能手动实现.
