python - 无法将 lxml etree 对象传递给单独的进程

标签 python lxml python-multiprocessing

我正在开发一个使用 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()

非常感谢任何帮助。

更新/回答

根据下一篇文章中的答案,我对其进行了一些修改,并设法让它在不使用字符串 IO 的情况下以低得多的内存占用工作。 etree.tostring 方法返回一个字节数组,它可以被 pickle,然后要 unpickle 字节数组可以被 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 并将文件名传递给子流程。

最佳答案

使用以下代码为 lxml Element/ElementTree 对象注册简单的 picklers/unpicklers。我过去将其与 lxml 和 zmq 一起使用。

import copy_reg
try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO
from lxml import etree

def element_unpickler(data):
    return etree.fromstring(data)

def element_pickler(element):
    data = etree.tostring(element)
    return element_unpickler, (data,)

copy_reg.pickle(etree._Element, element_pickler, element_unpickler)

def elementtree_unpickler(data):
    data = StringIO(data)
    return etree.parse(data)

def elementtree_pickler(tree):
    data = StringIO()
    tree.write(data)
    return elementtree_unpickler, (data.getvalue(),)

copy_reg.pickle(etree._ElementTree, elementtree_pickler, elementtree_unpickler)

关于python - 无法将 lxml etree 对象传递给单独的进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25991860/

相关文章:

python - Pandas 重新排列多索引系列

Python队列突然空了

Python 使用通配符在 XML 中查找标签

python - 如何通过 lxml 扩展函数模拟 XPath 2.0 函数?

python - 如果安装了特定模块,如何在 python 中提供某些功能?

python - 我可以在单核机器上运行多处理 Python 程序吗?

python - 如何限制没有终端或多处理库的 python 脚本使用的 CPU 数量?

python - 如何在 OpenCV 中重新绘制皮肤?

python - 如何处理动态语言中错误的参数类型?

python - MySQL服务器消失问题