파이썬에서 파일 잠금


152

파이썬으로 작성하려면 파일을 잠 가야합니다. 여러 Python 프로세스에서 한 번에 액세스합니다. 온라인에서 일부 솔루션을 찾았지만 대부분 유닉스 기반 또는 Windows 기반이기 때문에 대부분 내 목적에 실패합니다.

답변:


115

좋아, 그래서 나는 여기에 쓴 코드로 끝났고 , 내 웹 사이트 링크가 죽었습니다. archive.org ( GitHub에서도 가능)에서 볼 수 있습니다 . 다음과 같은 방식으로 사용할 수 있습니다.

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
블로그 게시물의 주석에서 알 수 있듯이이 솔루션은 잠금이 유지되는 방식으로 프로그램을 종료 할 수 있으며 파일 전에 잠금을 수동으로 삭제해야한다는 점에서 "완벽하지 않습니다" 다시 액세스 할 수있게됩니다. 그러나 그 점을 제외하고는 여전히 좋은 해결책입니다.
leetNightshade

3
그러나 에반의 FileLock의 또 다른 개선 된 버전은 여기에서 찾을 수 있습니다 : github.com/ilastik/lazyflow/blob/master/lazyflow/utility/...
스튜어트 버그

3
OpenStack은 자체적으로 (잘, Skip Montanaro의) 구현을 공개했습니다 -pylockfile- 이전 의견에서 언급 한 것과 매우 유사하지만 여전히 살펴볼 가치가 있습니다.
jweyrich

7
@jweyrich Openstacks pylockfile은 더 이상 사용되지 않습니다. 대신 패스너 또는 oslo.concurrency 를 사용하는 것이 좋습니다 .
harbun

2
내가 추측 한 또 다른 유사한 구현 : github.com/benediktschmitt/py-filelock
herry

39

크로스 플랫폼 파일 잠금 모듈이 있습니다 : Portalocker

Kevin이 말했듯이 한 번에 여러 프로세스에서 파일에 쓰는 것은 가능한 한 피하고 싶은 일입니다.

문제를 데이터베이스에 삽입 할 수 있다면 SQLite를 사용할 수 있습니다. 동시 액세스를 지원하고 자체 잠금을 처리합니다.


16
+1-SQLite는 거의 항상 이런 상황에 처해 있습니다.
cdleary

2
Portalocker에는 Windows 용 Python Extensions가 필요합니다.
n611x007

2
@naxa msvcrt와 ctype
Shmil The Cat

@ n611x007 Portalocker가 방금 업데이트되어 Windows에서 더 이상 확장이 필요하지 않습니다 :)
Wolph

2
SQLite는 동시 액세스를 지원합니까?
piotr

23

다른 솔루션은 많은 외부 코드 기반을 인용합니다. 직접 수행하려면 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사용할 수 있습니다 .withopen

경고 : 종료 가 호출 되기 전에 Windows에서 실행 중이고 Python이 충돌 하면 잠금 동작이 무엇인지 잘 모르겠습니다.

경고 : 여기에 제공된 잠금은 절대적인 것이 아니라 권고입니다. 잠재적으로 경쟁중인 모든 프로세스는 "AtomicOpen"클래스를 사용해야합니다.


unlock_file리눅스에서 파일 플래그 fcntl와 함께 다시 호출해서는 안 LOCK_UN됩니까?
eadmaster

파일 개체가 닫히면 잠금 해제가 자동으로 수행됩니다. 그러나 그것을 포함시키지 않는 것은 나쁜 프로그래밍 관행이었습니다. 코드를 업데이트하고 fcntl 잠금 해제 작업을 추가했습니다!
토마스 럭스

에서 __exit__당신 close외부 후 잠금 unlock_file. 런타임 중에 런타임이 데이터를 플러시 (즉, 쓰기) 할 수 있다고 생각합니다 close. 나는 일 개해야 믿을 flushfsync잠금 아래 중 잠금 외부에서 작성되어 있는지 추가 데이터를 만들기 위해 close.
벤자민 Bannier

정정 주셔서 감사합니다! 내가 거기 있다는 것을 확인하는 것입니다 포함하지 않는 경쟁 조건에 대한 가능성 flushfsync. 전화하기 전에 제안한 두 줄을 추가했습니다 unlock. 다시 테스트했는데 경쟁 조건이 해결 된 것으로 보입니다.
Thomas Lux

1
"잘못"되는 유일한 것은 프로세스 1이 파일을 잠그면 내용이 잘립니다 (내용이 지워짐)입니다. 잠금 전에 위의 코드에 "w"가있는 다른 파일 "open"을 추가하여 직접 테스트 할 수 있습니다 . 그러나 파일을 잠그기 전에 파일을 열어야하기 때문에 피할 수 없습니다. 명확히하기 위해 "원자"는 합법적 인 파일 내용 만 파일에서 찾을 수 있다는 의미입니다. 즉, 여러 경쟁 프로세스의 내용이 혼합 된 파일을 절대 얻을 수 없습니다.
Thomas Lux

15

lockfile을 선호합니다 — 플랫폼 독립적 인 파일 잠금


3
이 라이브러리는 잘 작성된 것처럼 보이지만 오래된 잠금 파일을 감지하는 메커니즘은 없습니다. 잠금을 작성한 PID를 추적하므로 해당 프로세스가 여전히 실행 중인지 알 수 있어야합니다.
sherbang

1
@sherbang : remove_existing_pidfile은 어떻습니까?
Janus Troelsen 2016 년

@JanusTroelsen은 pidlockfile 모듈이 원자 적으로 잠금을 획득하지 않습니다.
sherbang

@sherbang 확실합니까? O_CREAT | O_EXCL 모드로 잠금 파일을 엽니 다.
mhsmith 2016 년

2
이 라이브러리는 대체 된과의 일부인되어 있습니다 github.com/harlowja/fasteners
congusbongus

13

나는 그것을하기 위해 여러 가지 해결책을 찾고 있으며 내 선택은 오슬로입니다.

강력하고 비교적 잘 문서화되어 있습니다. 패스너를 기반으로합니다.

다른 솔루션 :

  • Portalocker : pywin32가 필요합니다. exe 설치이므로 pip를 통해 사용할 수 없습니다.
  • 패스너 : 제대로 문서화되지 않음
  • lockfile : 사용되지 않습니다
  • flufl.lock : POSIX 시스템을위한 NFS 안전 파일 잠금.
  • simpleflock : 마지막 업데이트 2013-07
  • zc.lockfile : 마지막 업데이트 2016-06 (2017-03 기준)
  • lock_file : 2007-10의 마지막 업데이트

re : Portalocker, 이제 pypiwin32 패키지를 통해 pip를 통해 pywin32를 설치할 수 있습니다.
Timothy Jannace


13

잠금은 플랫폼 및 장치마다 다르지만 일반적으로 몇 가지 옵션이 있습니다.

  1. flock () 또는 이와 동등한 기능을 사용하십시오 (os가 지원하는 경우). 잠금을 확인하지 않으면 무시되는 권고 잠금입니다.
  2. lock-copy-move-unlock 방법론을 사용하십시오. 여기서 파일을 복사하고 새 데이터를 작성한 다음 이동하십시오 (이동, 복사-이동은 Linux의 원자 작업입니다-OS 확인). 잠금 파일의 존재.
  3. 디렉토리를 "잠금"으로 사용하십시오. NFS는 flock ()을 지원하지 않기 때문에 NFS에 쓰는 경우 필요합니다.
  4. 프로세스간에 공유 메모리를 사용할 가능성도 있지만 시도한 적이 없습니다. OS에 따라 다릅니다.

이러한 모든 방법에 대해 잠금을 획득하고 테스트하려면 스핀 잠금 (실패 후 재시도) 기술을 사용해야합니다. 이로 인해 잘못된 동기화를위한 작은 창이 남지만 일반적으로 큰 문제가되지 않을 정도로 작습니다.

플랫폼 간 솔루션을 찾고 있다면 다른 메커니즘을 통해 다른 시스템에 로깅하는 것이 좋습니다 (다음의 가장 좋은 방법은 위의 NFS 기술입니다).

sqlite는 일반 파일의 NFS와 동일한 제약 조건을 따르므로 네트워크 공유의 sqlite 데이터베이스에 쓸 수 없으며 무료로 동기화 할 수 없습니다.


4
참고 : 이동 / 이름 바꾸기는 Win32에서 원 자성이 아닙니다. 참조 : stackoverflow.com/questions/167414/…
sherbang

4
새로운 참고 사항 : os.rename이제 Python 3.3 이후 Win32에서 원자 적입니다 : bugs.python.org/issue8828
Ghostkeeper

7

OS 수준에서 단일 파일에 대한 액세스 조정은 해결하고 싶지 않은 모든 종류의 문제로 가득 차 있습니다.

최선의 방법은 해당 파일에 대한 읽기 / 쓰기 액세스를 조정하는 별도의 프로세스입니다.


19
"해당 파일에 대한 읽기 / 쓰기 액세스를 조정하는 별도의 프로세스"-즉, 데이터베이스 서버를 구현하십시오. –-
Eli Bendersky

1
이것이 실제로 가장 좋은 대답입니다. "데이터베이스 서버 사용"을 말하는 것은 db가 항상 작업에 적합한 도구가 아니기 때문에 지나치게 단순화되었습니다. 일반 텍스트 파일이어야하는 경우 어떻게합니까? 좋은 해결책은 자식 프로세스를 생성 한 다음 명명 된 파이프, 유닉스 소켓 또는 공유 메모리를 통해 액세스하는 것입니다.
Brendon Crawford

9
설명이없는 FUD이므로 -1입니다. 쓰기 위해 파일을 잠그는 것은 OS가 그와 같은 기능을 제공한다는 매우 간단한 개념처럼 보입니다 flock. "자신의 뮤텍스와 그것들을 관리하기위한 데몬 프로세스를 롤링하는"접근 방식은 해결하기 위해 다소 극단적이고 복잡한 접근 방법처럼 보입니다.
Mark Amery

-Mark Amery가 제공 한 이유와 OP가 해결하고자하는 문제에 대한 확실한 의견 제시
Michael Krebs

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 .
monkut

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

편집 --- 위의 부실 잠금에 대한 일부 의견을 생각한 후 "잠금 파일"의 부실을 확인하기 위해 코드를 편집했습니다. 내 시스템 에서이 기능을 수천 번 반복하면 바로 직전부터 평균 0.002066 ... 초가 발생했습니다.

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

바로 다음에 :

remove('errloglock')

그래서 나는 부실을 나타내고 문제의 상황을 모니터링하기 위해 그 양의 5 배로 시작할 것이라고 생각했습니다.

또한 타이밍 작업을 할 때 실제로 필요하지 않은 약간의 코드가 있음을 깨달았습니다.

lock.close()

나는 열린 진술을 즉시 따랐 으므로이 편집에서 제거했습니다.


2

Evan Fossmark의 답변 에 추가하려면 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:블록 내의 모든 코드 는 스레드로부터 안전하므로 다른 프로세스가 파일에 액세스하기 전에 완료됩니다.


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

나는 간단하고 일한 (!) 구현을 발견했다.grizzled-python에서 을 .

간단한 사용 os.open (..., O_EXCL) + os.close ()는 Windows에서 작동하지 않았습니다.


4
O_EXCL 옵션은 잠금과 관련이 없습니다
Sergei

0

pylocker가 매우 유용 할 수 있습니다 . 파일을 잠 그거나 메커니즘을 잠그는 데 사용할 수 있으며 여러 Python 프로세스에서 한 번에 액세스 할 수 있습니다.

파일을 잠 그려면 다음과 같이하십시오.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.