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 を閉じ、ファイル名をサブプロセスに渡すことに戻りました。