我可以在Python中异步删除文件吗?

时间:2013-09-27 17:37:54

标签: python multithreading

我有一个长时间运行的python脚本,用于创建和删除临时文件。我注意到在文件删除上花费了大量的时间,但删除这些文件的唯一目的是确保程序在长时间运行期间最终不会填满所有磁盘空间。 Python中是否有一个跨平台机制来删除文件,以便在操作系统处理文件删除时主线程可以继续工作?

3 个答案:

答案 0 :(得分:11)

您可以尝试委派将文件删除到另一个线程或进程。

使用新生成的线程:

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))

流程版本可能允许更多的并行性,因为由于臭名昭着的global interpreter lock,Python线程没有并行执行。我希望GIL在调用任何阻塞内核函数时释放,例如unlink(),这样Python就可以让另一个线程取得进展。换句话说,调用os.unlink()的后台工作线程可能是最佳解决方案see Tim Peters' answer

然而,multiprocessing正在使用下面的Python线程与池中的进程异步通信,因此需要一些基准测试来确定哪个版本提供更多的并行性。

避免使用Python线程但需要更多编码的另一种方法是生成另一个进程并通过管道将文件名发送到其标准输入。这样您就可以将os.remove()交换为同步os.write()(一个write()系统调用)。它可以使用已弃用的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()

答案 1 :(得分:6)

您可以按照常见的生产者 - 消费者模式创建一个删除文件的线程:

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(全局解释器锁),因此这应该是有效的。

编辑 - 新文字

我建议不要每次删除产生一个新进程。在某些平台上,创建流程非常昂贵。还建议不要每次删除产生一个新线程:在一个长期运行的程序中,你真的不希望在任何时候创建无限数量的线程。根据文件删除请求的累积速度,可能会发生这种情况。

上面的“解决方案”比其他人更啰嗦,因为它避免了所有这些。总共只有一个新线程。当然,可以很容易地推广使用任何固定数量的线程,所有线程共享相同的dead_files队列。从1开始,如果需要,添加更多; - )

答案 2 :(得分:4)

OS级文件删除原语在Unix和Windows上都是同步的,所以我认为你几乎必须使用工作线程。您可以让它从Queue对象中删除文件,然后当主线程完成文件时,它可以将文件发布到队列中。如果您正在使用NamedTemporaryFile对象,则可能需要在构造函数中设置delete=False并将 name 发布到队列,而不是文件对象,因此您没有对象生命周期头痛。