Question

Je dois verrouiller un fichier pour l'écrire en Python. Il sera accessible à partir de plusieurs processus Python à la fois. J'ai trouvé des solutions en ligne, mais la plupart échouent car elles ne sont souvent que sous Unix ou Windows.

Était-ce utile?

La solution

D'accord, j'ai donc utilisé le code que j'ai écrit ici, sur mon site Web lien est mort, vue sur archive.org ( également disponible sur GitHub ). Je peux l'utiliser de la manière suivante:

from filelock import FileLock

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

Autres conseils

Il existe un module de verrouillage de fichiers multiplate-forme ici: Portalocker

Bien que, comme le dit Kevin, écrire dans un fichier à partir de plusieurs processus à la fois est quelque chose que vous voulez éviter autant que possible.

Si vous pouvez intégrer votre problème dans une base de données, vous pouvez utiliser SQLite. Il prend en charge les accès concurrents et gère son propre verrouillage.

Les autres solutions citent de nombreuses bases de code externes. Si vous préférez le faire vous-même, voici du code pour une solution multiplate-forme qui utilise les outils de verrouillage de fichiers respectifs sur les systèmes 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        

Maintenant, AtomicOpen peut être utilisé dans un bloc with dans lequel on utiliserait normalement une instruction open.

AVERTISSEMENT: Si l'exécution sur Windows et Python se bloque avant l'appel de exit , je ne suis pas sûr du comportement du verrou.

AVERTISSEMENT: Le verrouillage fourni ici est indicatif et non absolu. Tous les processus potentiellement concurrents doivent utiliser le & Quot; AtomicOpen & "; classe.

Je préfère fichier de verrouillage & # 8212; Verrouillage de fichier indépendant de la plate-forme

Le verrouillage dépend de la plate-forme et du périphérique, mais vous avez généralement quelques options:

  1. Utilisez flock () ou équivalent (si votre système d'exploitation le permet). Ceci est un verrouillage consultatif, à moins que vous ne vérifiiez le verrouillage, il est ignoré.
  2. Utilisez une méthodologie de verrouillage-copie-déplacement-déverrouillage dans laquelle vous copiez le fichier, écrivez les nouvelles données, puis déplacez-les (déplacement, pas copie - déplacement est une opération atomique sous Linux - vérifiez votre système d'exploitation), et vous vérifier l'existence du fichier de verrouillage.
  3. Utilisez un répertoire comme & "verrouiller &"; Cela est nécessaire si vous écrivez sur NFS, car NFS ne prend pas en charge flock ().
  4. Il existe également la possibilité d'utiliser la mémoire partagée entre les processus, mais je n'ai jamais essayé cela; c'est très spécifique à l'OS.

Pour toutes ces méthodes, vous devrez utiliser une technique d’essai et de test du verrou (réessayer après une erreur). Cela laisse une petite fenêtre pour une mauvaise synchronisation, mais il est généralement assez petit pour ne pas être un problème majeur.

Si vous recherchez une solution multi-plateforme, il est préférable de vous connecter à un autre système via un autre mécanisme (la meilleure solution est la technique NFS ci-dessus).

Notez que sqlite est soumis aux mêmes contraintes sur NFS que les fichiers normaux. Par conséquent, vous ne pouvez pas écrire dans une base de données sqlite sur un partage réseau et bénéficier de la synchronisation gratuitement.

J'ai examiné plusieurs solutions pour y parvenir et mon choix a été oslo.concurrency

C'est puissant et relativement bien documenté. Il est basé sur des attaches.

Autres solutions:

La coordination de l’accès à un seul fichier au niveau du système d’exploitation soulève de nombreux problèmes que vous ne souhaitez probablement pas résoudre.

Votre meilleur choix est d'avoir un processus séparé qui coordonne l'accès en lecture / écriture à ce fichier.

Le verrouillage d'un fichier est généralement une opération spécifique à la plate-forme. Il peut donc être nécessaire de prévoir la possibilité de l'exécuter sur différents systèmes d'exploitation. Par exemple:

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"

Je travaille dans une situation comme celle-ci, où je lance plusieurs copies du même programme à partir du même répertoire / dossier et en enregistrant les erreurs. Mon approche consistait à écrire un & Quot; fichier de verrouillage & Quot; sur le disque avant d'ouvrir le fichier journal. Le programme vérifie la présence du & Quot; fichier de verrouillage & Quot; avant de continuer et attend son tour si le " fichier verrouillé " existe.

Voici le code:

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 --- Après avoir réfléchi à certains des commentaires sur les verrous périmés ci-dessus, j'ai modifié le code pour ajouter une vérification de la minutie du & "Fichier de verrouillage". & "" Plusieurs milliers d’itérations de cette fonction ont été programmées sur mon système, soit une moyenne de 0,002066 secondes à peine d’avant:

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

juste après:

remove('errloglock')

alors j’ai pensé que je commencerais par 5 fois ce montant afin d’indiquer la stagnation et de suivre la situation à la recherche de problèmes.

De plus, en travaillant avec le timing, j'ai réalisé que j'avais un peu de code qui n'était pas vraiment nécessaire:

lock.close()

que j'avais immédiatement après l'instruction ouverte, je l'ai donc supprimée dans cette modification.

Le scénario est le suivant: L'utilisateur demande à un fichier de faire quelque chose. Ensuite, si l'utilisateur envoie à nouveau la même demande, il l'informe que la deuxième demande n'est pas terminée tant que la première demande n'est pas terminée. C’est pourquoi j’utilise un mécanisme de verrouillage pour traiter ce problème.

Voici mon code de travail:

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

J'ai trouvé une solution simple et efficace (!) mise en œuvre. de grizzled-python.

L'utilisation simple de os.open (..., O_EXCL) + os.close () ne fonctionnait pas sous Windows.

Vous pouvez trouver pylocker très utile. Il peut être utilisé pour verrouiller un fichier ou pour verrouiller des mécanismes en général et est accessible simultanément à partir de plusieurs processus Python.

Si vous souhaitez simplement verrouiller un fichier, voici comment cela fonctionne:

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top