multiprocessing.pool.MaybeEncodingError: 'TypeError("cannot serialize '_io.BufferedReader' object",)'

multiprocessing.pool.MaybeEncodingError: 'TypeError("cannot serialize '_io.BufferedReader' object",)'

为什么下面的代码只适用于 multiprocessing.dummy,而不适用于简单的 multiprocessing

import urllib.request
#from multiprocessing.dummy import Pool #this works
from multiprocessing import Pool

urls = ['http://www.python.org', 'http://www.yahoo.com','http://www.scala.org', 'http://www.google.com']

if __name__ == '__main__':
    with Pool(5) as p:
        results = p.map(urllib.request.urlopen, urls)

错误:

Traceback (most recent call last):
  File "urlthreads.py", line 31, in <module>
    results = p.map(urllib.request.urlopen, urls)
  File "C:\Users\patri\Anaconda3\lib\multiprocessing\pool.py", line 268, in map
    return self._map_async(func, iterable, mapstar, chunksize).get()
  File "C:\Users\patri\Anaconda3\lib\multiprocessing\pool.py", line 657, in get
    raise self._value
multiprocessing.pool.MaybeEncodingError: Error sending result: '[<http.client.HTTPResponse object at 0x0000016AEF204198>]'. Reason: 'TypeError("cannot serialize '_io.BufferedReader' object")'

缺少什么才能在没有 "dummy" 的情况下工作?

您从 urlopen() 返回的 http.client.HTTPResponse-对象附加了一个 _io.BufferedReader-对象,并且该对象不能被 pickle。

pickle.dumps(urllib.request.urlopen('http://www.python.org').fp)
Traceback (most recent call last):
...
    pickle.dumps(urllib.request.urlopen('http://www.python.org').fp)
TypeError: cannot serialize '_io.BufferedReader' object

multiprocessing.Pool 将需要对结果进行 pickle(序列化)以将其发送回父进程,这在这里失败了。由于dummy使用线程而不是进程,所以不会有酸洗,因为同一进程中的线程自然共享内存。

这个TypeError的一般解决方案是:

  1. 读出缓冲区并保存内容(如果需要)
  2. 从您尝试 pickle 的对象中删除对 '_io.BufferedReader' 的引用

在您的情况下,在 http.client.HTTPResponse 上调用 .read() 将清空并删除缓冲区,因此将响应转换为可腌制内容的函数可以简单地执行此操作:

def read_buffer(response):
    response.text = response.read()
    return response

示例:

r = urllib.request.urlopen('http://www.python.org')
r = read_buffer(r)
pickle.dumps(r)
# Out: b'\x80\x03chttp.client\nHTTPResponse\...

在考虑这种方法之前,请确保您确实想要使用多处理而不是多线程。对于 I/O-bound 任务,就像你在这里拥有的那样,多线程就足够了,因为大部分时间都花在等待响应上(不需要 cpu 时间)。涉及的多处理和 IPC 也会引入大量开销。