Domanda

Devo bloccare un file per scrivere in Python.Sarà possibile accedervi da più processi Python contemporaneamente.Ho trovato alcune soluzioni online, ma la maggior parte non soddisfa i miei scopi poiché spesso sono solo basate su Unix o Windows.

È stato utile?

Soluzione

Va bene, quindi ho finito con il codice che ho scritto qui, sul mio sito web il collegamento è morto, visualizza su archive.org (disponibile anche su GitHub).Posso usarlo nel modo seguente:

from filelock import FileLock

with FileLock("myfile.txt"):
    # work with the file as it is now locked
    print("Lock acquired.")

Altri suggerimenti

Esiste un modulo di blocco dei file multipiattaforma qui: Portalocker

Anche se come dice Kevin, scrivere su un file da più processi contemporaneamente è qualcosa che vuoi evitare se possibile.

Se riesci a trasformare il tuo problema in un database, puoi usare SQLite. Supporta l'accesso simultaneo e gestisce il proprio blocco.

Le altre soluzioni citano molte basi di codice esterne. Se preferisci farlo da solo, ecco un po 'di codice per una soluzione multipiattaforma che utilizza i rispettivi strumenti di blocco dei file su sistemi Linux / DOS.

try:
    # Posix based file locking (Linux, Ubuntu, MacOS, etc.)
    import fcntl, os
    def lock_file(f):
        fcntl.lockf(f, fcntl.LOCK_EX)
    def unlock_file(f):
        fcntl.lockf(f, fcntl.LOCK_UN)
except ModuleNotFoundError:
    # Windows file locking
    import msvcrt, os
    def file_size(f):
        return os.path.getsize( os.path.realpath(f.name) )
    def lock_file(f):
        msvcrt.locking(f.fileno(), msvcrt.LK_RLCK, file_size(f))
    def unlock_file(f):
        msvcrt.locking(f.fileno(), msvcrt.LK_UNLCK, file_size(f))


# Class for ensuring that all file operations are atomic, treat
# initialization like a standard call to 'open' that happens to be atomic.
# This file opener *must* be used in a "with" block.
class AtomicOpen:
    # Open the file with arguments provided by user. Then acquire
    # a lock on that file object (WARNING: Advisory locking).
    def __init__(self, path, *args, **kwargs):
        # Open the file and acquire a lock on the file before operating
        self.file = open(path,*args, **kwargs)
        # Lock the opened file
        lock_file(self.file)

    # Return the opened file object (knowing a lock has been obtained).
    def __enter__(self, *args, **kwargs): return self.file

    # Unlock the file and close the file object.
    def __exit__(self, exc_type=None, exc_value=None, traceback=None):        
        # Flush to make sure all buffered contents are written to file.
        self.file.flush()
        os.fsync(self.file.fileno())
        # Release the lock on the file.
        unlock_file(self.file)
        self.file.close()
        # Handle exceptions that may have come up during execution, by
        # default any exceptions are raised to the user.
        if (exc_type != None): return False
        else:                  return True        

Ora, AtomicOpen può essere usato in un blocco with dove normalmente si usa un'istruzione open.

ATTENZIONE: Se l'esecuzione su Windows e Python si arresta in modo anomalo prima che venga chiamata esci , non sono sicuro di quale sarebbe il comportamento del blocco.

ATTENZIONE: Il blocco fornito qui è consultivo, non assoluto. Tutti i processi potenzialmente concorrenti devono utilizzare la quot &; AtomicOpen & Quot; Classe.

Preferisco lockfile & # 8212; Blocco dei file indipendente dalla piattaforma

Il blocco è specifico per piattaforma e dispositivo, ma generalmente hai alcune opzioni:

  1. Usa flock () o equivalente (se il tuo sistema operativo lo supporta). Questo è un blocco di avviso, a meno che non si controlli il blocco, viene ignorato.
  2. Usa una metodologia di blocco-copia-sposta-sblocca, in cui copi il file, scrivi i nuovi dati, quindi spostali (sposta, non copia - lo spostamento è un'operazione atomica in Linux - controlla il tuo sistema operativo) e tu verifica l'esistenza del file di blocco.
  3. Utilizza una directory come " lock " ;. Questo è necessario se stai scrivendo su NFS, poiché NFS non supporta flock ().
  4. C'è anche la possibilità di utilizzare la memoria condivisa tra i processi, ma non l'ho mai provato; è molto specifico del sistema operativo.

Per tutti questi metodi, dovrai usare una tecnica spin-lock (retry-after-failure) per acquisire e testare il lock. Questo lascia una piccola finestra per una errata sincronizzazione, ma è generalmente abbastanza piccolo da non essere un grosso problema.

Se stai cercando una soluzione che sia multipiattaforma, allora è meglio accedere a un altro sistema tramite qualche altro meccanismo (la prossima cosa migliore è la tecnica NFS sopra).

Si noti che sqlite è soggetto agli stessi vincoli su NFS dei file normali, quindi non è possibile scrivere su un database sqlite su una condivisione di rete e ottenere la sincronizzazione gratuitamente.

Ho cercato diverse soluzioni per farlo e la mia scelta è stata oslo.concurrency

È potente e relativamente ben documentato. Si basa su elementi di fissaggio.

Altre soluzioni:

Il coordinamento dell'accesso a un singolo file a livello di sistema operativo è irto di tutti i tipi di problemi che probabilmente non si desidera risolvere.

La tua scommessa migliore è avere un processo separato che coordina l'accesso in lettura / scrittura a quel file.

Il blocco di un file è generalmente un'operazione specifica della piattaforma, quindi potrebbe essere necessario consentire la possibilità di essere eseguito su diversi sistemi operativi. Ad esempio:

import os

def my_lock(f):
    if os.name == "posix":
        # Unix or OS X specific locking here
    elif os.name == "nt":
        # Windows specific locking here
    else:
        print "Unknown operating system, lock unavailable"

Ho lavorato su una situazione come questa in cui eseguo più copie dello stesso programma all'interno della stessa directory / cartella ed errori di registrazione. Il mio approccio era quello di scrivere un & Quot; lock file & Quot; sul disco prima di aprire il file di registro. Il programma verifica la presenza del & Quot; blocca il file & Quot; prima di procedere e attende il suo turno se " blocca il file " esiste.

Ecco il codice:

def errlogger(error):

    while True:
        if not exists('errloglock'):
            lock = open('errloglock', 'w')
            if exists('errorlog'): log = open('errorlog', 'a')
            else: log = open('errorlog', 'w')
            log.write(str(datetime.utcnow())[0:-7] + ' ' + error + '\n')
            log.close()
            remove('errloglock')
            return
        else:
            check = stat('errloglock')
            if time() - check.st_ctime > 0.01: remove('errloglock')
            print('waiting my turn')

EDIT --- Dopo aver riflettuto su alcuni dei commenti sui blocchi non aggiornati sopra ho modificato il codice per aggiungere un controllo per la staleness del & Quot; lock file. & Quot; Il tempismo di diverse migliaia di iterazioni di questa funzione sul mio sistema ha dato e una media di 0,002066 ... secondi da poco prima:

lock = open('errloglock', 'w')

a subito dopo:

remove('errloglock')

quindi ho pensato che inizierò con 5 volte tale importo per indicare la stanchezza e monitorare la situazione per problemi.

Inoltre, mentre stavo lavorando con i tempi, mi sono reso conto che avevo un po 'di codice che non era davvero necessario:

lock.close()

che ho subito seguito alla dichiarazione aperta, quindi l'ho rimosso in questa modifica.

Lo scenario è così: L'utente richiede un file per fare qualcosa. Quindi, se l'utente invia nuovamente la stessa richiesta, informa l'utente che la seconda richiesta non viene eseguita fino al termine della prima richiesta. Ecco perché, uso il meccanismo di blocco per gestire questo problema.

Ecco il mio codice di lavoro:

from lockfile import LockFile
lock = LockFile(lock_file_path)
status = ""
if not lock.is_locked():
    lock.acquire()
    status = lock.path + ' is locked.'
    print status
else:
    status = lock.path + " is already locked."
    print status

return status

Ho trovato un'implementazione semplice e funzionante (!) da grizzled-python.

Uso semplice os.open (..., O_EXCL) + os.close () non ha funzionato su Windows.

Potresti trovare pylocker molto utile. Può essere usato per bloccare un file o per meccanismi di blocco in generale e vi si può accedere da più processi Python contemporaneamente.

Se vuoi semplicemente bloccare un file, ecco come funziona:

import uuid
from pylocker import Locker

#  create a unique lock pass. This can be any string.
lpass = str(uuid.uuid1())

# create locker instance.
FL = Locker(filePath='myfile.txt', lockPass=lpass, mode='w')

# aquire the lock
with FL as r:
    # get the result
    acquired, code, fd  = r

    # check if aquired.
    if fd is not None:
        print fd
        fd.write("I have succesfuly aquired the lock !")

# no need to release anything or to close the file descriptor, 
# with statement takes care of that. let's print fd and verify that.
print fd
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top