2009-01-28 10 views

Respuesta

7

El módulo shelve utiliza un paquete de base de datos subyacente (como dbm, gdbm o bsddb).

El restrictions pragraph dice (el subrayado es mío):

El módulo de estantería no admite simultáneas de lectura/escritura a los objetos dejados de lado. (Múltiples accesos simultáneos de lectura son seguros.) Cuando un programa tiene un estante abierto para escribir, ningún otro programa debe tenerlo abierto para lectura o escritura. El bloqueo de archivos Unix se puede usar para resolver esto, pero esto difiere en las versiones de Unix y requiere conocimiento sobre la implementación de la base de datos utilizada.

Conclusión: depende del sistema operativo y de la base de datos subyacente. Para mantener las cosas portátiles, no construya sobre concurrencia.

2

Según la respuesta superior, no es seguro tener múltiples escritores en la estantería. Mi enfoque para hacer que los estantes sean más seguros es escribir un envoltorio que se encargue de abrir y acceder a los elementos del estante. El código de contenedor es como la siguiente:

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() 
2

he implementado Ivo's approach como un gestor de contexto, para cualquier persona interesada:

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 
+0

por cierto pensé en la restricción de la comparación y de intercambio arriba- específica nivel de teclas, pero no estoy seguro de si la actualización de una clave puede no sobrescribir todas las demás y también puede depender de la base de datos subyacente, por lo tanto, mejor bloquear todo y estar a salvo –