代码之家  ›  专栏  ›  技术社区  ›  dbr

在python中创建竞争条件文件夹

  •  15
  • dbr  · 技术社区  · 16 年前

    我有一个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 对于 /tmp/example/ ,如果使用 /tmp/ 作为缓存目录 /tmp.lock

    总之,我需要缓存 urllib2 对光盘的响应。要做到这一点,我需要以多进程安全的方式访问一个已知的目录(如果需要,可以创建它)。它需要在OS X、Linux和Windows上工作。

    思想?我能想到的唯一替代解决方案是使用sqlite3存储而不是文件重写缓存模块。

    4 回复  |  直到 8 年前
        1
  •  11
  •   Community Mohan Dere    9 年前

    而不是

    if not os.path.exists(self.cache_location):
        os.mkdir(self.cache_location)
    

    你可以做到

    try:
        os.makedirs(self.cache_location)
    except OSError:
        pass
    

    因为你最终会得到同样的结果 功能 .

    免责声明:我不知道这可能是多么的蟒蛇。


    使用 SQLite3 , 可以 有点过分,但会增加 许多 功能性和灵活性。

    如果你要做大量的“选择”,同时插入和过滤,这是一个好主意 平方英寸3 因为它不会给简单的文件增加太多的复杂性(可以说它消除了复杂性)。


    重读你的问题(和评论),我能更好地理解你的问题。

    有什么可能 文件 可以创建相同的比赛条件吗?

    如果它足够小,那么我会做如下的事情:

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

        2
  •  10
  •   maazza    8 年前

    我最后得到的代码是:

    import os
    import errno
    
    folder_location = "/tmp/example_dir"
    
    try:
        os.mkdir(folder_location)
    except OSError as e:
        if e.errno == errno.EEXIST and os.path.isdir(folder_location):
            # File exists, and it's a directory,
            # another process beat us to creating this dir, that's OK.
            pass
        else:
            # Our target dir exists as a file, or different error,
            # reraise the error!
            raise
    
        3
  •  2
  •   pythonic metaphor    16 年前

    您能否捕获异常,然后测试该文件是否作为目录存在?

        4
  •  2
  •   John La Rooy    16 年前

    当你有比赛条件时,EAFP(比许可更容易请求原谅)比LBYL(跳跃前先看)更有效。

    Error checking strategies