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

Ric*_*ich 6 python multithreading

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

Max*_*kin 11

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

使用新生成的线程:

thread.start_new_thread(os.remove, filename)
Run Code Online (Sandbox Code Playgroud)

或者,使用一个过程:

# 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))
Run Code Online (Sandbox Code Playgroud)

流程版本可能允许更多的并行性,因为由于臭名昭着的全局解释器锁定, Python线程不会并行执行.我希望GIL在调用任何阻塞内核函数时释放,例如unlink(),以便Python允许另一个线程取得进展.换句话说,调用后台工作者线程os.unlink()可能是最好的解决方案,请参阅Tim Peters的回答.

然而,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()
Run Code Online (Sandbox Code Playgroud)

  • @octopusgrabbus我没回答吗? (2认同)

Tim*_*ers 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()
Run Code Online (Sandbox Code Playgroud)

CPython围绕内部文件删除调用释放GIL(全局解释器锁),因此这应该是有效的.

编辑 - 新文本

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

上面的"解决方案"比其他解决方案更加晦涩,因为它避免了所有这些.总共只有一个新线程.当然,很容易将其概括为使用任何固定数量的线程,而是共享相同的dead_files队列.从1开始,如果需要,添加更多;-)