Python'a yazmak için bir dosyayı kilitlemem gerekiyor. Bir kerede birden fazla Python işleminden erişilebilir. Çevrimiçi olarak bazı çözümler buldum, ancak çoğu zaman yalnızca Unix tabanlı veya Windows tabanlı oldukları için çoğu zaman başarısızım.
Python'a yazmak için bir dosyayı kilitlemem gerekiyor. Bir kerede birden fazla Python işleminden erişilebilir. Çevrimiçi olarak bazı çözümler buldum, ancak çoğu zaman yalnızca Unix tabanlı veya Windows tabanlı oldukları için çoğu zaman başarısızım.
Yanıtlar:
Tamam, bu yüzden burada yazdığım kodla devam ettim , web sitemde link öldü, archive.org'da ( GitHub'da da mevcut ) görüntüleyin. Aşağıdaki şekilde kullanabilirim:
from filelock import FileLock
with FileLock("myfile.txt.lock"):
print("Lock acquired.")
with open("myfile.txt"):
# work with the file as it is now locked
Burada bir çapraz platform dosya kilitleme modülü var: Portalocker
Kevin'in dediği gibi, bir kerede birden fazla işlemden bir dosyaya yazmak mümkünse kaçınmak istediğiniz bir şeydir.
Sorununuzu bir veritabanına bağlayabiliyorsanız SQLite kullanabilirsiniz. Eşzamanlı erişimi destekler ve kendi kilitlemesini gerçekleştirir.
Diğer çözümler çok sayıda harici kod tabanından bahsediyor. Bunu kendiniz yapmayı tercih ederseniz, Linux / DOS sistemlerinde ilgili dosya kilitleme araçlarını kullanan çapraz platform çözümü için bazı kodlar.
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
Şimdi, normalde bir ifade kullanacak bir blokta AtomicOpen
kullanılabilir .with
open
UYARI: Çıkış çağrılmadan önce Windows ve Python'da çöküyorsa , kilit davranışının ne olacağından emin değilim.
UYARI: Burada sağlanan kilitleme mutlak değil tavsiye niteliğindedir. Potansiyel olarak rekabet edebilecek tüm süreçler "AtomicOpen" sınıfını kullanmalıdır.
unlock_file
linux dosya bayrak fcntl
ile tekrar aramak gerekir LOCK_UN
?
__exit__
size close
dışarıda sonra kilidin unlock_file
. Çalışma zamanı sırasında verileri akıtıp (yani, yazabilir) inanıyorum close
. Bir zorunluluk inanıyoruz flush
ve fsync
kilit altında sırasında kilit dış yazılır emin hiçbir ek verileri yapmak close
.
flush
ve fsync
. Aramadan önce önerdiğiniz iki hattı ekledim unlock
. Yeniden test ettim ve yarış durumu çözülmüş görünüyor.
Lockfile'ı tercih ederim - Platformdan bağımsız dosya kilitleme
Bunu yapmak için çeşitli çözümlere bakıyorum ve seçimim oslo.concurrency oldu.
Güçlü ve nispeten iyi belgelenmiş. Bağlantı elemanlarına dayanır.
Diğer çözümler:
Kilitleme platforma ve cihaza özeldir, ancak genellikle birkaç seçeneğiniz vardır:
Tüm bu yöntemler için, kilidi elde etmek ve test etmek için bir döndürme kilidi (arıza sonrası yeniden deneme) tekniği kullanmanız gerekir. Bu, yanlış senkronizasyon için küçük bir pencere bırakır, ancak genellikle büyük bir sorun olmayacak kadar küçüktür.
Çapraz platform olan bir çözüm arıyorsanız, başka bir sistemle başka bir sisteme giriş yapmaktan daha iyidir (bir sonraki en iyi şey yukarıdaki NFS tekniğidir).
Sqlite'ın normal dosyalardaki NFS ile aynı kısıtlamalara tabi olduğunu unutmayın, bu nedenle bir ağ paylaşımındaki sqlite veritabanına yazamaz ve ücretsiz senkronizasyon elde edemezsiniz.
os.rename
Python 3.3'ten beri Win32'de atomik: bugs.python.org/issue8828
İşletim sistemi düzeyinde tek bir dosyaya erişimi koordine etmek, muhtemelen çözmek istemediğiniz her türlü sorunla doludur.
En iyi bahsiniz, o dosyaya okuma / yazma erişimini koordine eden ayrı bir işlemdir.
flock
bunun gibi işlevler sundukları için oldukça basit bir kavram gibi görünüyor . "Kendi muteksilerinizi yuvarlayın ve onları yönetmek için bir daemon süreci" yaklaşımı, çözmemiz için oldukça aşırı ve karmaşık bir yaklaşım gibi görünüyor ... aslında bize söylemediğiniz bir sorun var, ama sadece korkutucu bir şekilde var.
Bir dosyayı kilitlemek genellikle platforma özgü bir işlemdir, bu nedenle farklı işletim sistemlerinde çalışma olasılığına izin vermeniz gerekebilir. Örneğin:
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"
Aynı programın birden çok kopyasını aynı dizin / klasörden ve günlük hatalarından çalıştırdığım böyle bir durum üzerinde çalışıyorum. Benim yaklaşım günlük dosyasını açmadan önce diske bir "kilit dosyası" yazmak oldu. Program devam etmeden önce "kilit dosyası" olup olmadığını kontrol eder ve "kilit dosyası" mevcutsa sırasını bekler.
İşte kod:
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')
DÜZENLEME --- Yukarıdaki eski kilitler hakkında bazı yorumlar üzerinde düşündükten sonra ben "kilit dosyası" staleness için bir kontrol eklemek için kodu düzenledi. Sistemimde bu işlevin birkaç bin yinelemesini zamanlama ve hemen önce gelen ortalama 0.002066 ... saniye verdi:
lock = open('errloglock', 'w')
hemen sonra:
remove('errloglock')
bu yüzden 5 kez başlayacağımı düşündüm.
Ayrıca, zamanlama ile çalışırken, gerçekten gerekli olmayan bir kod biraz olduğunu fark ettim:
lock.close()
hangi hemen açık ifadeyi takip vardı, bu yüzden bu düzenlemede kaldırdım.
Üzerine eklemek için Evan Fossmark cevabı , burada nasıl kullanılacağına ilişkin bir örnek FileLock :
from filelock import FileLock
lockfile = r"c:\scr.txt"
lock = FileLock(lockfile + ".lock")
with lock:
file = open(path, "w")
file.write("123")
file.close()
with lock:
Blok içindeki herhangi bir kod iş parçacığı için güvenlidir, yani başka bir işlemin dosyaya erişmeden önce biteceği anlamına gelir.
senaryo böyledir: Kullanıcı şey yapmak için bir dosyasını ister. Daha sonra kullanıcı aynı isteği tekrar gönderirse, ilk istek bitene kadar ikinci isteğin yapılmadığını kullanıcıya bildirir. Bu yüzden bu sorunu çözmek için kilit mekanizması kullanıyorum.
İşte benim çalışma kodu:
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
Pylocker'ı çok faydalı bulabilirsiniz . Bir dosyayı kilitlemek veya genel olarak kilitleme mekanizmaları için kullanılabilir ve aynı anda birden fazla Python işleminden erişilebilir.
Bir dosyayı kilitlemek istiyorsanız şu şekilde çalışır:
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