Domanda sul modulo di Python shelve
Domanda
Il modulo shelve di Python ha una protezione integrata per assicurarsi che due processi non stiano scrivendo su un file contemporaneamente?
Soluzione
Il modulo shelve utilizza un pacchetto di database sottostante (come dbm, gdbm o bsddb).
La restrizioni paragrafo dice (il mio enfasi):
Il modulo shelve non supporta l'accesso simultaneo in lettura / scrittura agli oggetti accantonati . (Gli accessi di lettura simultanei multipli sono sicuri.) Quando un programma ha uno scaffale aperto per la scrittura, nessun altro programma dovrebbe averlo aperto per la lettura o la scrittura. Il blocco dei file Unix può essere utilizzato per risolvere questo problema, ma questo differisce tra le versioni di Unix e richiede conoscenze sull'implementazione del database utilizzata.
Conclusione: dipende dal sistema operativo e dal DB sottostante. Per mantenere le cose portatili, non basarsi sulla concorrenza.
Altri suggerimenti
Secondo la risposta in alto, non è sicuro avere più autori a disposizione. Il mio approccio per rendere più sicuri gli scaffali è scrivere un involucro che si occupi dell'apertura e dell'accesso agli elementi dello scaffale. Il codice wrapper è simile al seguente:
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()
Ho implementato L'approccio di Ivo come gestore del contesto, per chiunque interessati:
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