我有一个 urllib2 缓存模块,它偶尔会因为以下代码而崩溃:
if not os.path.exists(self.cache_location):
os.mkdir(self.cache_location)
问题是,第二行执行的时候,文件夹可能已经存在,会报错:
File ".../cache.py", line 103, in __init__ os.mkdir(self.cache_location) OSError: [Errno 17] File exists: '/tmp/examplecachedir/'
这是因为脚本被我无法控制的第三方代码同时启动了很多次。
可以找到代码(在我尝试修复错误之前)here, on github
我无法使用 tempfile.mkstemp ,因为它通过使用随机命名的目录 ( tempfile.py source here ) 来解决竞争条件,这将破坏缓存的目的。
我不想简单地丢弃错误,因为如果文件夹名称作为文件存在(不同的错误),则会引发相同的错误 Errno 17 错误,例如:
$ touch blah $ python >>> import os >>> os.mkdir("blah") Traceback (most recent call last): File "", line 1, in OSError: [Errno 17] File exists: 'blah' >>>
我无法使用 threading.RLock
,因为代码是从多个进程调用的。
因此,我尝试编写一个简单的基于文件的锁(that version can be found here),但这有一个问题:它创建了上一层的锁文件,所以 /tmp/example.lock
for /tmp/example/
,如果您使用 /tmp/
作为缓存目录(因为它试图制作 /tmp.lock
),它会中断。 .
简而言之,我需要将 urllib2
响应缓存到磁盘。为此,我需要以多进程安全方式访问已知目录(如果需要,创建它)。它需要在 OS X、Linux 和 Windows 上运行。
想法?我能想到的唯一替代解决方案是使用 SQLite3 存储而不是文件重写缓存模块。
最佳答案
代替
if not os.path.exists(self.cache_location):
os.mkdir(self.cache_location)
你可以做
try:
os.makedirs(self.cache_location)
except OSError:
pass
因为您最终会得到相同的功能。
免责声明:我不知道这可能有多 Pythonic。
使用 SQLite3
,可能有点矫枉过正,但会为您的用例增加很多功能和灵 active 。 p>
如果你必须做大量的“选择”、并发插入和过滤,那么使用 SQLite3
是个好主意,因为它不会比简单的文件增加太多复杂性(这可能是有争议的它消除了复杂性)。
重读你的问题(和评论)我可以更好地理解你的问题。
文件产生相同竞争条件的可能性有多大?
如果它足够小,那么我会做类似的事情:
if not os.path.isfile(self.cache_location):
try:
os.makedirs(self.cache_location)
except OSError:
pass
另外,阅读你的代码,我会改变
else:
# Our target dir is already a file, or different error,
# relay the error!
raise OSError(e)
到
else:
# Our target dir is already a file, or different error,
# relay the error!
raise
因为这确实是您想要的,Python 重新引发完全相同的异常(只是吹毛求疵)。
还有一件事,可能是this可能对你有用(仅限类 Unix)。
关于python - Python 中的竞争条件创建文件夹,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1586648/