Pregunta

¿El módulo de estantería Python tiene alguna protección integrada para asegurarse de que dos procesos no estén escribiendo en un archivo al mismo tiempo?

¿Fue útil?

Solución

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

El restricciones pragraph dice (mi énfasis):

  

El módulo de almacenamiento no admite acceso de lectura / escritura concurrente a objetos archivados . (Múltiples accesos de lectura simultáneos son seguros). Cuando un programa tiene un estante abierto para escribir, ningún otro programa debe tenerlo abierto para leer o escribir. El bloqueo de archivos Unix se puede utilizar para resolver esto, pero esto difiere entre las versiones de Unix y requiere conocimiento sobre la implementación de la base de datos utilizada.

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

Otros consejos

Según la respuesta principal, 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 contenedor que se encargue de abrir y acceder a los elementos de los estantes. El código contenedor se parece a esto:

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

He implementado el enfoque de Ivo como administrador de contexto, para cualquier persona interesado:

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top