पायथन में एक फ़ाइल को लॉक करना


152

मुझे पायथन में लिखने के लिए एक फ़ाइल लॉक करने की आवश्यकता है। इसे एक बार में कई पायथन प्रक्रियाओं से एक्सेस किया जाएगा। मुझे कुछ समाधान ऑनलाइन मिले हैं, लेकिन मेरे उद्देश्यों के लिए सबसे असफल हैं क्योंकि वे अक्सर केवल यूनिक्स आधारित या विंडोज आधारित हैं।

जवाबों:


115

ठीक है, इसलिए मैंने अपने द्वारा लिखे गए कोड के साथ जाना समाप्त कर दिया, मेरी वेबसाइट पर लिंक मर चुका है, संग्रह.org पर देखें ( गीथहब पर भी उपलब्ध है )। मैं निम्नलिखित फैशन में इसका उपयोग कर सकता हूं:

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

10
जैसा कि ब्लॉग पोस्ट में एक टिप्पणी द्वारा उल्लेख किया गया है, यह समाधान "सही" नहीं है, इसमें प्रोग्राम को इस तरह से समाप्त करना संभव है कि लॉक को जगह में छोड़ दिया जाए और आपको फ़ाइल से पहले लॉक को मैन्युअल रूप से हटाना होगा फिर से सुलभ हो जाता है। हालांकि, एक तरफ, यह अभी भी एक अच्छा समाधान है।
leNNightshade

3
इवान की फाइललॉक का एक और बेहतर संस्करण यहां पाया जा सकता है: github.com/ilastik/lazyflow/blob/master/lazyflow/utility/…
स्टुअर्ट बर्ग

3
ओपनस्टैक ने अपना स्वयं का प्रकाशन किया (ठीक है, स्किप मोंट्रो के) कार्यान्वयन - पाइलकफाइल - पिछली टिप्पणियों में उल्लिखित लोगों के समान, लेकिन फिर भी एक बार देखने लायक है।
jweyrich

7
@jweyrich Openstacks pylockfile अब पदावनत हो गया है। इसके बजाय फास्टनरों या ऑस्लो.केंकेंसी का उपयोग करने की सलाह दी जाती है ।
हरबुन

2
इसी तरह का एक और कार्यान्वयन मुझे लगता है: github.com/benediktschmitt/py-filelock
herry

39

यहाँ एक क्रॉस-प्लेटफ़ॉर्म फ़ाइल लॉकिंग मॉड्यूल है: Portalocker

यद्यपि केविन कहते हैं, एक बार में कई प्रक्रियाओं से किसी फ़ाइल पर लिखना कुछ ऐसा है जिससे आप बचना चाहते हैं।

यदि आप अपनी समस्या को एक डेटाबेस में शामिल कर सकते हैं, तो आप SQLite का उपयोग कर सकते हैं। यह समवर्ती पहुंच का समर्थन करता है और अपनी लॉकिंग को संभालता है।


16
+1 - SQLite लगभग हमेशा इस प्रकार की स्थितियों में जाने का मार्ग है।
cdleary

2
Portalocker को उस पर विंडोज के लिए पायथन एक्सटेंशन की आवश्यकता होती है।
n611x007

2
@naxa इसका एक प्रकार है जो केवल msvcrt और ctypes पर निर्भर करता है, देखें राउंडअप.hg.sourceforge.net/hgweb/roundup/roundup/roundup/file/tip/…
Shmil The Cat

@ n611x007 Portalocker को अभी अपडेट किया गया है, इसलिए इसे विंडोज पर किसी भी एक्सटेंशन की आवश्यकता नहीं है :)
वोल्फ

2
SQLite समवर्ती पहुँच का समर्थन करता है?
पोट्ट्रर

23

अन्य समाधान बहुत सारे बाह्य कोड आधारों का हवाला देते हैं। यदि आप इसे स्वयं करना पसंद करेंगे, तो यहां क्रॉस-प्लेटफ़ॉर्म समाधान के लिए कुछ कोड दिए गए हैं जो लिनक्स / एनओएस सिस्टम पर संबंधित फ़ाइल लॉकिंग टूल का उपयोग करते हैं।

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बयान का उपयोग करेगा ।

चेतावनी: यदि निकास से पहले विंडोज और पायथन क्रैश पर चल रहा है, तो मुझे यकीन नहीं है कि लॉक व्यवहार क्या होगा।

चेतावनी: यहां दी गई लॉकिंग सलाहकार है, निरपेक्ष नहीं। सभी संभावित प्रतिस्पर्धी प्रक्रियाओं को "एटॉमिकऑपेन" वर्ग का उपयोग करना चाहिए।


unlock_fileलिनक्स पर फाइल को झंडे के fcntlसाथ फिर से कॉल नहीं करना चाहिए LOCK_UN?
.मास्टर

अनलॉक स्वचालित रूप से तब होता है जब फ़ाइल ऑब्जेक्ट बंद हो जाता है। हालाँकि, इसे शामिल न करना मेरे लिए खराब प्रोग्रामिंग प्रथा थी। मैंने कोड अपडेट किया है और fcntl अनलॉक ऑपरेशन जोड़ा है!
थॉमस लक्स

में __exit__आप closeबाहर के बाद लॉक की unlock_file। मेरा मानना ​​है कि रनटाइम के दौरान डेटा फ्लश (यानी, लिख) सकता है close। मेरा मानना ​​है कि किसी भी अतिरिक्त डेटा के दौरान लॉक के बाहर नहीं लिखा है यह सुनिश्चित करने के लिए एक flushऔर fsyncलॉक के नीचे होना चाहिए close
बेंजामिन बैनियर

सुधारों के लिए धन्यवाद! मैं सत्यापित है कि है बिना एक रेस स्थिति के लिए संभावना flushऔर fsync। मैंने कॉल करने से पहले आपके द्वारा सुझाई गई दो पंक्तियों को जोड़ दिया है unlock। मैंने फिर से परीक्षण किया और दौड़ की स्थिति हल होती दिखाई दे रही है।
थॉमस लक्स

1
केवल एक चीज जो "गलत" जाएगी वह यह है कि समय प्रक्रिया 1 से फ़ाइल लॉक हो जाती है, इसकी सामग्री को काट दिया जाएगा (सामग्री को मिटा दिया जाएगा)। आप लॉक से पहले ऊपर दिए गए कोड के लिए "w" के साथ एक और फाइल "ओपन" जोड़कर खुद इसका परीक्षण कर सकते हैं । हालांकि यह अपरिहार्य है, क्योंकि आपको फ़ाइल को लॉक करने से पहले खोलना होगा। स्पष्ट करने के लिए, "परमाणु" इस अर्थ में है कि एक फ़ाइल में केवल वैध फ़ाइल सामग्री मिलेगी। इसका मतलब यह है कि आपको कभी भी एक साथ मिश्रित कई प्रतिस्पर्धी प्रक्रियाओं की सामग्री के साथ एक फ़ाइल नहीं मिलेगी।
थॉमस लक्स

15

मैं लॉकफाइल पसंद करता हूं - प्लेटफ़ॉर्म-स्वतंत्र फ़ाइल लॉकिंग


3
यह लाइब्रेरी अच्छी तरह से लिखी हुई लगती है, लेकिन बासी लॉक फ़ाइलों का पता लगाने के लिए कोई तंत्र नहीं है। यह लॉक बनाने वाले PID को ट्रैक करता है, इसलिए यह बताना संभव है कि क्या यह प्रक्रिया अभी भी चल रही है।
शेरबंग 19

1
@sherbang: के बारे में क्या remove_existing_pidfile ?
Janus Troelsen

@JususTroelsen pidlockfile मॉड्यूल एटॉमिक रूप से लॉक हासिल नहीं करता है।
शेरबंग

@sherbang क्या आप सुनिश्चित हैं? यह O_CREAT | O_EXCL मोड के साथ लॉक फ़ाइल को खोलता है।
मुहस्तिथ

2
कृपया ध्यान दें कि इस पुस्तकालय को
सुपरटेक

13

मैं ऐसा करने के लिए कई समाधानों को देख रहा हूं और मेरी पसंद है oslo.concurrency रही है

यह शक्तिशाली और अपेक्षाकृत अच्छी तरह से प्रलेखित है। यह फास्टनरों पर आधारित है।

अन्य उपाय:

  • Portalocker : आवश्यकता है, जो एक exe स्थापना है, इसलिए पाइप के माध्यम से संभव नहीं है
  • फास्टनर : खराब दस्तावेज
  • lockfile : पदावनत
  • flufl.lock : सिस्टम के लिए सुरक्षित फ़ाइल लॉकिंग।
  • simpleflock : अंतिम अद्यतन 2013-07
  • zc.lockfile : अंतिम अपडेट 2016-06 (2017-03 तक)
  • लॉक_फाइल : 2007-10 में अंतिम अपडेट

पुन: Portalocker, अब आप pypiwin32 पैकेज के माध्यम से पाइप के माध्यम से pywin32 स्थापित कर सकते हैं।
तीमुथियुस Jannace


13

लॉकिंग प्लेटफ़ॉर्म और डिवाइस विशिष्ट है, लेकिन आम तौर पर, आपके पास कुछ विकल्प होते हैं:

  1. झुंड (), या समतुल्य का उपयोग करें (यदि आपका ओएस इसका समर्थन करता है)। यह सलाहकार लॉकिंग है, जब तक आप लॉक की जांच नहीं करते हैं, इसकी अनदेखी की जाती है।
  2. लॉक-कॉपी-मूव-अनलॉक-कार्यप्रणाली का उपयोग करें, जहां आप फ़ाइल की प्रतिलिपि बनाते हैं, नया डेटा लिखते हैं, फिर इसे स्थानांतरित करते हैं (ले जाएँ, प्रतिलिपि नहीं - चालन लिनक्स में एक परमाणु ऑपरेशन है - अपने OS की जाँच करें), और आप जाँच करें लॉक फ़ाइल का अस्तित्व।
  3. एक निर्देशिका का उपयोग "लॉक" के रूप में करें। यह आवश्यक है यदि आप NFS को लिख रहे हैं, क्योंकि NFS झुंड () का समर्थन नहीं करता है।
  4. प्रक्रियाओं के बीच साझा मेमोरी का उपयोग करने की संभावना भी है, लेकिन मैंने कभी कोशिश नहीं की है; यह बहुत ओएस-विशिष्ट है।

इन सभी तरीकों के लिए, आपको लॉक प्राप्त करने और परीक्षण करने के लिए स्पिन-लॉक (रिट्री-आफ्टर-फेल) तकनीक का उपयोग करना होगा। यह गलत सिंक्रनाइज़ेशन के लिए एक छोटी सी खिड़की छोड़ देता है, लेकिन इसका आम तौर पर छोटा होना एक बड़ा मुद्दा नहीं है।

यदि आप एक ऐसे समाधान की तलाश कर रहे हैं जो क्रॉस प्लेटफॉर्म है, तो आप किसी अन्य तंत्र के माध्यम से किसी अन्य सिस्टम में प्रवेश करना बेहतर समझते हैं (अगली सबसे अच्छी बात ऊपर एनएफएस तकनीक है)।

ध्यान दें कि sqlite NFS पर समान बाधाओं के अधीन है जो सामान्य फाइलें हैं, इसलिए आप नेटवर्क शेयर पर एक sqlite डेटाबेस में नहीं लिख सकते हैं और मुफ्त में सिंक्रनाइज़ेशन प्राप्त कर सकते हैं।


4
नोट: Win32 में चाल / नाम परमाणु नहीं है। संदर्भ: stackoverflow.com/questions/167414/…
sherbang

4
नया नोट: os.renameपायथन 3.3 के बाद से अब Win32 में परमाणु है: Bugs.python.org/issue8828
भूतकीपर

7

OS स्तर पर किसी एकल फ़ाइल तक पहुँच को समन्वयित करना उन सभी प्रकार के मुद्दों से भरा हुआ है जिन्हें आप शायद हल नहीं करना चाहते हैं।

आपकी सबसे अच्छी शर्त एक अलग प्रक्रिया है जो उस फ़ाइल के पढ़ने / लिखने का समन्वय करती है।


19
"अलग-अलग प्रक्रिया जो उस फ़ाइल तक पहुंच को पढ़ने / लिखने का समन्वय करती है" - दूसरे शब्दों में, एक डेटाबेस सर्वर लागू करें :-)
एली बेंडस्की

1
यह वास्तव में सबसे अच्छा जवाब है। केवल कहने के लिए "डेटाबेस सर्वर का उपयोग करें" अत्यधिक सरल है, क्योंकि एक डीबी हमेशा नौकरी के लिए सही उपकरण नहीं होने वाला है। क्या होगा अगर इसे एक सादे पाठ फ़ाइल होने की आवश्यकता है? एक अच्छा समाधान एक बच्चे की प्रक्रिया को स्पॉन करना और फिर एक नामित पाइप, यूनिक्स सॉकेट या साझा मेमोरी के माध्यम से एक्सेस करना हो सकता है।
ब्रेंडन क्रॉफर्ड

9
-1 क्योंकि यह स्पष्टीकरण के बिना सिर्फ FUD है। लिखने के लिए एक फ़ाइल को लॉक करना मेरे लिए एक बहुत ही सीधी अवधारणा की तरह लगता है कि OSes flockइसके लिए कार्यों के साथ पेश करता है। "अपने स्वयं के म्यूटेक्स को रोल करें और उन्हें प्रबंधित करने के लिए एक डेमन प्रक्रिया" को हल करने के लिए लेने के बजाय एक चरम और जटिल दृष्टिकोण की तरह लगता है ... एक समस्या जिसे आपने वास्तव में हमारे बारे में नहीं बताया है, लेकिन सिर्फ सुझाव दिया गया है।
मार्क एमी

-1 @ मर्के एमी द्वारा दिए गए कारणों के लिए, साथ ही साथ एक बेबाक राय पेश करने के लिए जिसके बारे में ओपी हल करना चाहता है
माइकल क्रेब्स

5

किसी फ़ाइल को लॉक करना आमतौर पर एक प्लेटफ़ॉर्म-विशिष्ट ऑपरेशन होता है, इसलिए आपको अलग-अलग ऑपरेटिंग सिस्टम पर चलने की संभावना के लिए अनुमति देनी पड़ सकती है। उदाहरण के लिए:

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"

7
आप यह पहले से ही जानते होंगे, लेकिन रनिंग प्लेटफॉर्म पर जानकारी प्राप्त करने के लिए प्लेटफ़ॉर्म मॉड्यूल भी उपलब्ध है। platform.system ()। docs.python.org/library/platform.html
मोनकुट

2

मैं इस तरह की स्थिति पर काम कर रहा हूं जहां मैं एक ही निर्देशिका / फ़ोल्डर और लॉगिंग त्रुटियों के भीतर से एक ही कार्यक्रम की कई प्रतियां चलाता हूं। मेरा दृष्टिकोण लॉग फाइल खोलने से पहले डिस्क को "लॉक फाइल" लिखना था। कार्यक्रम आगे बढ़ने से पहले "लॉक फाइल" की उपस्थिति की जांच करता है, और अगर "लॉक फाइल" मौजूद है, तो अपनी बारी का इंतजार करता है।

यहाँ कोड है:

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 --- ऊपर बासी ताले के बारे में कुछ टिप्पणियों पर विचार करने के बाद मैंने "लॉक फ़ाइल" की गतिहीनता के लिए एक चेक जोड़ने के लिए कोड को संपादित किया। मेरे सिस्टम पर इस फ़ंक्शन के कई हज़ार पुनरावृत्तियों को दिया गया और 0.002066 का औसत ... कुछ ही समय पहले:

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

बस के बाद:

remove('errloglock')

इसलिए मुझे लगा कि मैं 5 बार उस राशि के साथ शुरू करूंगा जो कि गतिहीनता को इंगित करती है और समस्याओं के लिए स्थिति की निगरानी करती है।

इसके अलावा, जैसा कि मैं समय के साथ काम कर रहा था, मुझे एहसास हुआ कि मेरे पास थोड़ा सा कोड था जो वास्तव में आवश्यक नहीं था:

lock.close()

जो मैंने तुरंत खुले कथन का पालन किया था, इसलिए मैंने इसे इस संपादन में हटा दिया है।


2

इवान फॉसमार्क के उत्तर को जोड़ने के लिए , यहां एक उदाहरण दिया गया है कि फिलालॉक का उपयोग कैसे किया जाए :

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:ब्लॉक के भीतर कोई भी कोड थ्रेड-सुरक्षित है, जिसका अर्थ है कि यह किसी अन्य प्रक्रिया से फ़ाइल तक पहुंचने से पहले समाप्त हो जाएगा।


1

परिदृश्य है कि तरह है: उपयोगकर्ता कुछ करने के लिए एक फ़ाइल अनुरोध करता है। फिर, यदि उपयोगकर्ता फिर से वही अनुरोध भेजता है, तो यह उपयोगकर्ता को सूचित करता है कि पहला अनुरोध पूरा होने तक दूसरा अनुरोध नहीं किया गया है। इसीलिए, मैं इस मुद्दे को संभालने के लिए लॉक-मैकेनिज्म का उपयोग करता हूं।

यहाँ मेरा काम कोड है:

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

0

मुझे एक साधारण और काम मिला (!) घड़ियाल-अजगर से लागू हुआ।

सरल उपयोग os.open (..., O_EXCL) + os.close () विंडोज़ पर काम नहीं किया।


4
O_EXCL विकल्प लॉक से संबंधित नहीं है
सर्गेई

0

आपको plocklocker बहुत उपयोगी लग सकता है। इसका उपयोग किसी फाइल को लॉक करने के लिए या सामान्य तौर पर लॉकिंग मैकेनिज्म के लिए किया जा सकता है और एक ही बार में कई पायथन प्रक्रियाओं से इसे एक्सेस किया जा सकता है।

यदि आप बस एक फ़ाइल को यहाँ लॉक करना चाहते हैं तो यह कैसे काम करता है:

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.