我正在开发一个项目,以使用 lxml 在 python 中同时解析多个 xml 文件。当我初始化进程时,我希望我的主类在将 etree 对象传递给进程之前对 XML 做一些工作,但我发现当 etree 对象到达新进程时,类仍然存在,但 XML 从在对象中,getroot() 返回 None。
我知道我只能使用队列传递可选数据,但是我传递给“args”字段内的进程的情况也是如此吗?
这是我的代码:
import multiprocessing, multiprocessing.pool, time
from lxml import etree
def compute(tree):
print("Start Process")
print(type(tree)) # Returns <class 'lxml.etree._ElementTree'>
print(id(tree)) # Returns new ID 44637320 as expected
print(tree.getroot()) # Returns None
def pool_init(queue):
# see http://stackoverflow.com/a/3843313/852994
compute.queue = queue
class Main():
def __init__(self):
pass
def main(self):
tree = etree.parse('test.xml')
print(id(tree)) # Returns object ID 43998536
print(tree.getroot()) #Returns <Element SymCLI_ML at 0x29f5dc8>
self.queue = multiprocessing.Queue()
self.pool = multiprocessing.Pool(processes=1, initializer=pool_init, initargs=(self.queue,))
self.pool.apply_async(func=compute, args=(tree,))
time.sleep(10)
if __name__ == '__main__':
Main().main()
非常感谢任何和所有帮助。
更新/回答
根据下一篇文章中的答案,我对其进行了一些修改,并设法使其在不使用 String IO 的情况下以更低的内存占用工作。etree.tostring 方法返回一个字节数组,它可以被腌制,然后解除腌制,字节数组可以被 etree 解析。
import multiprocessing, multiprocessing.pool, time, copyreg
from lxml import etree
def compute(tree):
print("Start Process")
print(type(tree)) # Returns <class 'lxml.etree._ElementTree'>
print(tree.getroot()) # Returns <Element SymCLI_ML at 0x29f5dc8>. Success!
def pool_init(queue):
# see http://stackoverflow.com/a/3843313/852994
compute.queue = queue
def elementtree_unpickler(data):
return etree.parse(BytesIO(data))
def elementtree_pickler(tree):
return elementtree_unpickler, (etree.tostring(tree),)
copyreg.pickle(etree._ElementTree, elementtree_pickler, elementtree_unpickler)
class Main():
def __init__(self):
pass
def main(self):
tree = etree.parse('test.xml')
print(tree.getroot()) #Returns <Element SymCLI_ML at 0x29f5dc8>
self.queue = multiprocessing.Queue()
self.pool = multiprocessing.Pool(processes=1, initializer=pool_init, initargs=(self.queue,))
self.pool.apply_async(func=compute, args=(tree,))
time.sleep(10)
if __name__ == '__main__':
Main().main()
更新 2
在对内存进行了一些基准测试后,我发现传递大对象会导致对象无法通过主进程上的垃圾收集来清除。这可能不是小规模的问题,但 etree 对象在内存中的数量级为数百 MB。一旦在语句中使用 XML 对象调用了异步任务,如果从主进程中删除该对象,即使我手动调用垃圾收集,也无法从内存中清除该对象。因此,我恢复到在主进程中关闭 XML 并将文件名传递给子进程。