可以工作。内存用于以线程安全的方式跨多个进程写入公共缓存。在什么情况下(如果有的话)会失败或导致错误?
库首先写入临时文件,然后将临时文件移动到目标。源代码:
在同一个操作系统的进程中,写临时文件似乎是安全的,因为它在文件名中包含了pid。此外,它在同一进程中的线程之间似乎是安全的,因为它包含线程id。来源:def _concurrency_safe_write(self, to_write, filename, write_func): """Writes an object into a file in a concurrency-safe way.""" temporary_filename = concurrency_safe_write(to_write, filename, write_func) self._move_item(temporary_filename, filename)
def concurrency_safe_write(object_to_write, filename, write_func): """Writes an object into a unique file in a concurrency-safe way.""" thread_id = id(threading.current_thread()) temporary_filename = '{}.thread-{}-pid-{}'.format( filename, thread_id, os.getpid()) write_func(object_to_write, temporary_filename) return temporary_filename
将临时文件移动到目标在Windows上显示问题。来源:
if os.name == 'nt': # https://github.com/joblib/joblib/issues/540 access_denied_errors = (5, 13) from os import replace def concurrency_safe_rename(src, dst): """Renames ``src`` into ``dst`` overwriting ``dst`` if it exists. On Windows os.replace can yield permission errors if executed by two different processes. """ max_sleep_time = 1 total_sleep_time = 0 sleep_time = 0.001 while total_sleep_time < max_sleep_time: try: replace(src, dst) break except Exception as exc: if getattr(exc, 'winerror', None) in access_denied_errors: time.sleep(sleep_time) total_sleep_time += sleep_time sleep_time *= 2 else: raise else: raise else: from os import replace as concurrency_safe_rename # noqa
从这个源代码中可以看到,在Windows上,由于访问拒绝错误,在总时间1秒内无法将临时文件移动到目标,并且使用指数回退重试,它可能会失败。
相同的源代码有一个指向问题#540的链接,该问题描述了Windows错误,并以注释关闭:
由#541修复(希望)。
,(希望)";在评论中,作者似乎不能保证修复是最终的,但这个问题没有重新打开,所以它可能不会再发生。
对于其他操作系统,没有特殊的逻辑或重试,只使用标准的os.replace()。描述中提到了"可能失败"的情况。而且它将是一个原子操作
将src文件或目录重命名为dst。如果dst是目录,则会引发OSError。如果dst存在并且是一个文件,如果用户有权限,它将被静默替换。如果src和dst在不同的文件系统上,则操作可能失败。如果成功,重命名将是一个原子操作(这是POSIX的要求)。
如果没有人在更改目标目录中的权限,则不应该太担心此操作失败的可能性。"如果src和dst在不同的文件系统上"的场景;似乎不可行,因为源路径(临时文件)只是通过向目标路径添加后缀来构建的,所以它们应该在同一个目录中。
关于rename的原子性的其他问题:
- rename()是原子的吗?
- mv是原子在我的fs?
- 标准要求重命名是原子的吗?