سؤال

أحتاج إلى قفل ملف للكتابة في بايثون.سيتم الوصول إليه من خلال عمليات Python المتعددة في وقت واحد.لقد وجدت بعض الحلول عبر الإنترنت، ولكن معظمها يفشل في تحقيق أهدافي لأنها غالبًا ما تكون مستندة إلى Unix أو Windows فقط.

هل كانت مفيدة؟

المحلول

حسنا، حتى انتهى بي الأمر برمز كتبت <الإضراب> <وأ href = "http://www.evanfosmark.com/2009/01/cross-platform-file-locking-support-in-python / "يختلط =" noreferrer "> هنا، على موقعي في <لأ href =" https://web.archive.org/web/20140531203736/http://www.evanfosmark راسي: 80/2009/01 / عبر منصة ملف تأمين الدعم في والثعبان / "يختلط =" noreferrer "> رابط ميت، وعرض على archive.org (<ل أ href = "https://github.com/dmfrey/FileLock" يختلط = "noreferrer"> تتوفر أيضا على جيثب ). أنا يمكن استخدامه في الشكل التالي:

from filelock import FileLock

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

نصائح أخرى

وهناك وحدة ملف عبر منصة قفل هنا: Portalocker

وعلى الرغم من أن كما يقول كيفن والكتابة إلى ملف من عمليات متعددة في آن واحد هو شيء كنت ترغب في تجنب إذا كان ذلك ممكنا.

إذا يمكنك نعل مشكلتك في قاعدة بيانات، يمكنك استخدام برنامج SQLite. وهو يدعم الوصول المتزامن ويتعامل مع قفل الخاص بها.

تستشهد الحلول الأخرى بالكثير من قواعد التعليمات البرمجية الخارجية.إذا كنت تفضل القيام بذلك بنفسك، فإليك بعض التعليمات البرمجية لحل عبر الأنظمة الأساسية يستخدم أدوات قفل الملفات المعنية على أنظمة 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        

الآن، AtomicOpen يمكن استخدامها في with كتلة حيث يستخدم المرء عادةً ملف open إفادة.

تحذير: إذا تعطل التشغيل على Windows وPython من قبل مخرج تم استدعاؤه، لست متأكدًا من سلوك القفل.

تحذير: القفل المقدم هنا استشاري وليس مطلقًا.يجب على كافة العمليات المتنافسة استخدام فئة "AtomicOpen".

وأنا أفضل على lockfile - يمكنك منصة مستقلة ملف تأمين

القفل خاص بالنظام الأساسي والجهاز، ولكن بشكل عام، لديك بعض الخيارات:

  1. استخدم قطيع () أو ما يعادله (إذا كان نظام التشغيل الخاص بك يدعمه).هذا قفل استشاري، إلا إذا قمت بالتحقق من القفل، فسيتم تجاهله.
  2. استخدم منهجية القفل والنسخ والنقل وفتح القفل، حيث تقوم بنسخ الملف، وكتابة البيانات الجديدة، ثم نقله (نقل، وليس نسخ - النقل هو عملية ذرية في Linux - تحقق من نظام التشغيل الخاص بك)، وتتحقق من وجود وجود ملف القفل.
  3. استخدم الدليل باعتباره "قفل".يعد هذا ضروريًا إذا كنت تكتب إلى NFS، نظرًا لأن NFS لا يدعم القطيع ().
  4. هناك أيضًا إمكانية استخدام الذاكرة المشتركة بين العمليات، لكنني لم أحاول ذلك مطلقًا؛إنه خاص جدًا بنظام التشغيل.

بالنسبة لجميع هذه الطرق، سيتعين عليك استخدام تقنية قفل الدوران (إعادة المحاولة بعد الفشل) للحصول على القفل واختباره.وهذا يترك نافذة صغيرة لسوء المزامنة، ولكنها صغيرة بشكل عام بما يكفي بحيث لا تمثل مشكلة كبيرة.

إذا كنت تبحث عن حل متعدد المنصات، فمن الأفضل أن تقوم بتسجيل الدخول إلى نظام آخر عبر آلية أخرى (الشيء التالي الأفضل هو تقنية NFS المذكورة أعلاه).

لاحظ أن sqlite يخضع لنفس القيود على NFS التي تخضع لها الملفات العادية، لذلك لا يمكنك الكتابة إلى قاعدة بيانات sqlite على مشاركة الشبكة والحصول على المزامنة مجانًا.

لقد كنت أبحث في العديد من الحلول للقيام بذلك وكان خياريoslo.concurrency

إنها قوية وموثقة جيدًا نسبيًا.يعتمد على السحابات.

حلول أخرى:

وتنسيق الوصول إلى ملف واحد على مستوى OS محفوف بكل أنواع المشكلات التي ربما لا يريدون حل.

وأفضل رهان هو ان تكون هناك عملية منفصلة إحداثيات الوصول للقراءة / الكتابة إلى هذا الملف.

وقفل ملف عادة ما تكون عملية منصة محددة، لذلك قد تحتاج إلى السماح لإمكانية تشغيلها على أنظمة تشغيل مختلفة. على سبيل المثال:

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"

لقد كنت أعمل على مثل هذا الموقف حيث أقوم بتشغيل نسخ متعددة من نفس البرنامج من داخل نفس الدليل/المجلد وأخطاء التسجيل.كان أسلوبي هو كتابة "ملف قفل" على القرص قبل فتح ملف السجل.يتحقق البرنامج من وجود "ملف القفل" قبل المتابعة، وينتظر دوره في حالة وجود "ملف القفل".

هنا هو الرمز:

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

تحرير --- بعد التفكير في بعض التعليقات حول الأقفال التي لا معنى لها أعلاه ، قمت بتحرير الكود لإضافة شيك لعلاج "ملف القفل". توقيت عدة آلاف من التكرارات من هذه الوظيفة على نظامي أعطى ومتوسط ​​0.002066 ...ثواني من قبل:

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

إلى بعد:

remove('errloglock')

لذلك اعتقدت أنني سأبدأ بخمسة أضعاف هذا المبلغ للإشارة إلى الجمود ومراقبة الوضع بحثًا عن المشاكل.

أيضًا، عندما كنت أعمل على التوقيت، أدركت أن لدي القليل من التعليمات البرمجية التي لم تكن ضرورية حقًا:

lock.close()

والذي قمت به مباشرة بعد البيان المفتوح، لذلك قمت بإزالته في هذا التعديل.

ال سيناريو هو مثل هذا:يطلب المستخدم ملفًا للقيام بشيء ما.ثم، إذا أرسل المستخدم نفس الطلب مرة أخرى، فإنه يعلم المستخدم أن الطلب الثاني لن يتم حتى انتهاء الطلب الأول.لهذا السبب، أستخدم آلية القفل للتعامل مع هذه المشكلة.

هنا هو رمز العمل الخاص بي:

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

ولقد وجدت بسيطة وعملت (!) التنفيذ من أشهب-الثعبان.

وبسيط استخدام os.open (...، O_EXCL) + os.close () لا يعمل على ويندوز.

وأنت قد تجد pylocker مفيدة جدا. ويمكن استخدامه لقفل ملف أو لتأمين الآليات بشكل عام ويمكن الوصول إليها من العمليات بيثون متعددة في وقت واحد.

إذا كنت تريد ببساطة لقفل ملف هنا كيف يعمل:

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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top