【问题标题】:Python shelve module questionPython搁置模块问题
【发布时间】:2009-01-28 04:32:14
【问题描述】:

Python shelve 模块是否有任何内置保护措施来确保两个进程不会同时写入文件?

【问题讨论】:

    标签: python shelve


    【解决方案1】:

    搁置模块使用底层数据库包(如 dbm、gdbm 或 bsddb)。

    restrictions pragraph 说(我的重点):

    搁置模块不支持对搁置对象的并发读/写访问。 (多个同时读取访问是安全的。)当一个程序有一个架子可供写入时,任何其他程序都不应该打开它来读取或写入。可以使用 Unix 文件锁定来解决这个问题,但这在 Unix 版本之间有所不同,并且需要了解所使用的数据库实现。

    结论:这取决于操作系统和底层数据库。为了保持可移植性,不要建立在并发之上。

    【讨论】:

      【解决方案2】:

      根据最佳答案,将多个作家搁置是不安全的。我使货架更安全的方法是编写一个包装器来处理打开和访问货架元素。包装器代码如下所示:

      def open(self, mode=READONLY):
          if mode is READWRITE:
              lockfilemode = "a" 
              lockmode = LOCK_EX
              shelve_mode = 'c'
          else:
              lockfilemode = "r"
              lockmode = LOCK_SH
              shelve_mode = 'r'
          self.lockfd = open(shelvefile+".lck", lockfilemode)
          fcntl.flock(self.lockfd.fileno(), lockmode | LOCK_NB)
          self.shelve = shelve.open(shelvefile, flag=shelve_mode, protocol=pickle.HIGHEST_PROTOCOL))
      def close(self):
          self.shelve.close()
          fcntl.flock(self.lockfd.fileno(), LOCK_UN)
          lockfd.close()
      

      【讨论】:

        【解决方案3】:

        我已将Ivo's approach 实现为上下文管理器,供任何感兴趣的人使用:

        from contextlib import contextmanager, closing
        from fcntl import flock, LOCK_SH, LOCK_EX, LOCK_UN
        import shelve
        
        @contextmanager
        def locking(lock_path, lock_mode):
            with open(lock_path, 'w') as lock:
                flock(lock.fileno(), lock_mode) # block until lock is acquired
                try:
                    yield
                finally:
                    flock(lock.fileno(), LOCK_UN) # release
        
        class DBManager(object):
            def __init__(self, db_path):
                self.db_path = db_path
        
            def read(self):
                with locking("%s.lock" % self.db_path, LOCK_SH):
                    with closing(shelve.open(self.db_path, "c", 2)) as db:
                        return dict(db)
        
            def cas(self, old_db, new_db):
                with locking("%s.lock" % self.db_path, LOCK_EX):
                    with closing(shelve.open(self.db_path, "c", 2)) as db:
                        if old_db != dict(db):
                            return False
                        db.clear()
                        db.update(new_db)
                        return True
        

        【讨论】:

        • 顺便说一句,我想将比较和交换限制为特定的顶级键,但我不确定更新一个键是否可能不会覆盖所有其他键,它也可能取决于底层数据库所以最好锁定整个东西并确保安全
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-20
        • 2012-11-14
        • 2015-09-13
        相关资源
        最近更新 更多