Python多进程 - logging.FileHandler对象引发PicklingError
看起来,logging
模块中的处理器和multiprocessing
的工作不太兼容:
import functools
import logging
import multiprocessing as mp
logger = logging.getLogger( 'myLogger' )
handler = logging.FileHandler( 'logFile' )
def worker( x, handler ) :
print x ** 2
pWorker = functools.partial( worker, handler=handler )
#
if __name__ == '__main__' :
pool = mp.Pool( processes=1 )
pool.map( pWorker, range(3) )
pool.close()
pool.join()
输出:
cPickle.PicklingError: Can't pickle <type 'thread.lock'>: attribute lookup thread.lock failed
如果我把pWorker
换成以下任意一种方法,就不会出现错误了:
# this works
def pWorker( x ) :
worker( x, handler )
# this works too
pWorker = functools.partial( worker, handler=open( 'logFile' ) )
我其实不太明白PicklingError
是什么。是不是因为logging.FileHandler
这个类的对象不能被“打包”?(我在网上查过,但没找到相关信息)
1 个回答
2
FileHandler
对象内部使用了一个叫threading.Lock
的东西来确保多个线程在写入时不会互相干扰。不过,threading.Lock
返回的thread.lock
对象不能被“打包”,这就意味着它不能在不同的进程之间传递,而这正是通过pool.map
把它传给子进程所需要的。
在multiprocessing
的文档中,有一部分专门讲了如何在使用multiprocessing
时进行日志记录,你可以在这里找到。简单来说,你需要让子进程继承父进程的日志记录器,而不是试图通过调用map
来显式地传递日志记录器或处理器。
不过需要注意的是,在Linux上,你可以这样做:
from multiprocessing import Pool
import logging
logger = logging.getLogger( 'myLogger' )
def worker(x):
print handler
print x **2
def initializer(handle):
global handler
handler = handle
if __name__ == "__main__":
handler = logging.FileHandler( 'logFile' )
#pWorker = functools.partial( worker, handler=handler )
pool = Pool(processes=4, initializer=initializer, initargs=(handler,))
pool.map(worker, range(3))
pool.close()
pool.join
initializer
和initargs
用于在每个子进程启动时运行一个方法。由于os.fork
的工作方式,这在Linux上允许处理器通过继承的方式进入子进程。但是,在Windows上是行不通的;因为Windows不支持os.fork
,所以仍然需要将handler
打包才能通过initargs
传递。