一時ファイルを作成および削除する、長時間実行される python スクリプトがあります。ファイルの削除にかなりの時間が費やされていることに気付きましたが、それらのファイルを削除する唯一の目的は、長期実行中にプログラムが最終的にすべてのディスク領域をいっぱいにしないようにすることです。OSがファイルの削除を処理している間、メインスレッドが動作し続けることができるように、Pythonにファイルを無秩序に削除するためのクロスプラットフォームメカニズムがありますか?
3 に答える
ファイルの削除を別のスレッドまたはプロセスに委譲してみることができます。
新しく生成されたスレッドを使用する:
thread.start_new_thread(os.remove, filename)
または、プロセスを使用して:
# create the process pool once
process_pool = multiprocessing.Pool(1)
results = []
# later on removing a file in async fashion
# note: need to hold on to the async result till it has completed
results.append(process_pool.apply_async(os.remove, filename), callback=lambda result: results.remove(result))
悪名高いグローバル インタープリター ロックが原因で Python スレッドが並列で実行されないため、プロセス バージョンではより多くの並列処理が可能になる場合があります。unlink()
ただし、Python が別のスレッドを進行できるように、GIL が などのブロッキング カーネル関数を呼び出したときに解放されることを期待します。つまり、呼び出すバックグラウンド ワーカー スレッドがos.unlink()
最適なソリューションである可能性があります。Tim Peters の回答を参照してください。
それでも、multiprocessing
プール内のプロセスと非同期に通信するために下にある Python スレッドを使用しているため、どのバージョンがより多くの並列処理を提供するかを判断するには、いくつかのベンチマークが必要です。
Python スレッドの使用を避ける代わりに、より多くのコーディングが必要になる別の方法は、別のプロセスを生成し、ファイル名をパイプ経由でその標準入力に送信することです。このようにos.remove()
して、同期os.write()
(1 つのwrite()
システムコール) に交換します。deprecated を使用して行うことができます。os.popen()
この関数の使用は、子プロセスに対して一方向でのみ通信するため、完全に安全です。実用的なプロトタイプ:
#!/usr/bin/python
from __future__ import print_function
import os, sys
def remover():
for line in sys.stdin:
filename = line.strip()
try:
os.remove(filename)
except Exception: # ignore errors
pass
def main():
if len(sys.argv) == 2 and sys.argv[1] == '--remover-process':
return remover()
remover_process = os.popen(sys.argv[0] + ' --remover-process', 'w')
def remove_file(filename):
print(filename, file=remover_process)
remover_process.flush()
for file in sys.argv[1:]:
remove_file(file)
if __name__ == "__main__":
main()
一般的な生産者と消費者のパターンに従って、ファイルを削除するスレッドを作成できます。
import threading, Queue
dead_files = Queue.Queue()
END_OF_DATA = object() # a unique sentinel value
def background_deleter():
import os
while True:
path = dead_files.get()
if path is END_OF_DATA:
return
try:
os.remove(path)
except: # add the exceptions you want to ignore here
pass # or log the error, or whatever
deleter = threading.Thread(target=background_deleter)
deleter.start()
# when you want to delete a file, do:
# dead_files.put(file_path)
# when you want to shut down cleanly,
dead_files.put(END_OF_DATA)
deleter.join()
CPython は内部ファイル削除呼び出しの周りで GIL (グローバル インタープリター ロック) を解放するので、これは効果的です。
編集 - 新しいテキスト
削除ごとに新しいプロセスを生成しないことをお勧めします。一部のプラットフォームでは、プロセスの作成に非常にコストがかかります。また、削除ごとに新しいスレッドを生成しないことをお勧めします。実行時間の長いプログラムでは、任意の時点で無制限の数のスレッドを作成する可能性はまったく必要ありません。ファイル削除リクエストがどれだけ早く積み重なるかによって、ここで発生する可能性があります.
上記の「解決策」は、それをすべて回避するため、他のものよりも冗長です。新しいスレッドは合計で 1 つだけです。もちろん、代わりに任意の固定数のスレッドを使用して、すべて同じdead_files
キューを共有するように一般化することは簡単にできます。1から始めて、必要に応じてさらに追加してください;-)
OS レベルのファイル削除プリミティブは、Unix と Windows の両方で同期的であるため、ほぼワーカー スレッドを使用する必要があると思います。ファイルをプルして Queue オブジェクトから削除し、メイン スレッドがファイルを処理し終わったら、そのファイルをキューにポストすることができます。NamedTemporaryFile オブジェクトを使用している場合は、おそらくコンストラクターで設定し、ファイル オブジェクトではなく名前delete=False
をキューにポストするだけで済みます。これにより、オブジェクトの有効期間の問題を回避できます。