推荐阅读:http://luly.lamost.org/blog/python_multiprocessing.html
1. 参数共享
1.1 进程间传递的所有参数必须是可以被pickle的
- 可以被pickle的python对象类型:(没找到之前那个链接。。。)
- 被pickle的对象(比如说其中包含有一棵树)的递归层次有限制,可以用
sys.setrecursionlimit(n)
来改变,默认的是1000。 - 包裹在
Manager().dict()
或者Manager().list()
中的对象也必须是可以被pickle的。 - 当自定义类的对象不能被pickle时,可以利用
__getstate__()
将其中无法被pickle的属性去掉,同时用__setstate__()
在unpickle时找回来,具体参见:https://blog.csdn.net/xuelians/article/details/79999275
1.2 Manager()
可以配合进程池使用,但是注意:Manager().dict()
返回的是DictProxy
,不是dict
。直接遍历其中的元素会出现以下错误:
my_dict = Manager().dict()
for i in my_dict:
print(i)
# 错误如下:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 2, in __getitem__
File "/usr/lib/python2.6/multiprocessing/managers.py", line 740, in _callmethod
raise convert_to_error(kind, result)
KeyError: 2
这是因为DictProxy没有实现iterkeys(),具体解释见http://m.newsmth.net/article/Python/100915。所以可以这么写:
for i in my_dict.keys():
# ...
1.3 内存共享sharedmem(避免pickle)
共享内存中只能存放numpy数组,而且多维数组我没实践出来。。。参考文档:http://rainwoodman.github.io/sharedmem/
2. 多进程打印日志
- 根据官方文档的介绍,logging 是线程安全的,也就是说,在一个进程内的多个线程同时往同一个文件写日志是安全的。但是多个进程往同一个文件写日志不是安全的。
为了解决这个问题,可以使用 ConcurrentLogHandler,ConcurrentLogHandler 可以在多进程环境下安全的将日志写入到同一个文件,并且可以在日志文件达到特定大小时,分割日志文件。在默认的 logging 模块中,有个 TimedRotatingFileHandler 类,可以按时间分割日志文件,可惜 ConcurrentLogHandler 不支持这种按时间分割日志文件的方式。
安装:pip install ConcurrentLogHandler
使用:from cloghandler import ConcurrentRotatingFileHandler
(以上参考:https://www.cnblogs.com/restran/p/4743840.html) - 安装及使用:
(1)windows下安装时还需要安装pypiwin32。
(2)使用时出现了进程卡住的情况,查证之后发现是windows下的lock无法正常的获取和释放。
(3)未解决(2)中问题,可以使用GitHub上一位老哥修复过的package,名为concurrent_log_handler:
安装pip install concurrent_log_handler
运行后会自动创建对应的.lock文件,通过锁的方式来安全地写日志文件。
from concurrent_log_handler import ConcurrentRotatingFileHandler
def set_mp_logger(log_file, ch_flag=True, fh_flag=True):
"""
将logger信息输出到log_file文件中
:param log_file:
:return:
"""
logger = logging.getLogger(os.path.basename(log_file).replace('.log', ''))
formatter = logging.Formatter(fmt='[%(name)s]: %(asctime)s - %(filename)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %A')
logger.setLevel(logging.DEBUG)
fh = ConcurrentRotatingFileHandler(log_file, maxBytes=1024*1024, backupCount=2, encoding='utf-8')
fh.setLevel(logging.INFO)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(formatter)
fh.setFormatter(formatter)
if ch_flag is True:
logger.addHandler(ch)
if fh_flag is True:
logger.addHandler(fh)
return logger
my_logger = set_mp_logger(os.path.join(const.PROJECT_PATH, 'log', 'my_logger.log'))