6

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 メソッドはバイト配列を返します。これは 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 を閉じ、ファイル名をサブプロセスに渡すことに戻りました。

4

1 に答える 1