Pythonで書き込むためにファイルをロックする必要があります。一度に複数のPythonプロセスからアクセスされます。オンラインでいくつかの解決策を見つけましたが、それらは多くの場合UnixベースまたはWindowsベースのみであるため、ほとんどの場合私の目的に失敗します。
Pythonで書き込むためにファイルをロックする必要があります。一度に複数のPythonプロセスからアクセスされます。オンラインでいくつかの解決策を見つけましたが、それらは多くの場合UnixベースまたはWindowsベースのみであるため、ほとんどの場合私の目的に失敗します。
回答:
さて、私がここで書いたコードを書き終えました。私のウェブサイトのリンクは死んでいます。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
ここにクロスプラットフォームのファイルロックモジュールがあります: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で実行していて、exitが呼び出される前にPythonがクラッシュした場合、ロックの動作がどうなるかわかりません。
警告:ここで提供されるロックは、絶対的なものではなく、助言的なものです。競合する可能性のあるすべてのプロセスは、「AtomicOpen」クラスを使用する必要があります。
unlock_file
Linux上のファイルfcntl
はLOCK_UN
フラグで再び呼び出すべきではありませんか?
__exit__
、あなたclose
の外側のロックの後にunlock_file
。ランタイムは中にデータをフラッシュ(つまり、書き込み)できると思いますclose
。私は、ロック中にロックの外に追加のデータが書き込まれないようにするために、ロックの下にいる必要があるflush
と思います。fsync
close
flush
fsync
unlock
私が好むロックファイルプラットフォームに依存しないファイルロック-
私はそれを行うためのいくつかの解決策を見てきましたが、私の選択は oslo.concurrencyでした
それは強力であり、比較的よく文書化されています。それはファスナーに基づいています。
その他の解決策:
ロックはプラットフォームとデバイスに固有ですが、通常、いくつかのオプションがあります。
これらすべての方法では、ロックの取得とテストにスピンロック(失敗後の再試行)テクニックを使用する必要があります。これは、誤同期のための小さなウィンドウを残しますが、一般的には大きな問題にならないほど十分に小さいです。
クロスプラットフォームのソリューションを探している場合は、他のメカニズムを使用して別のシステムにログを記録することをお勧めします(次の最良の方法は、上記のNFSテクニックです)。
sqliteは通常のファイルと同じNFS上の制約を受けるため、ネットワーク共有上のsqliteデータベースに書き込み、無料で同期を取得することはできません。
os.rename
Python 3.3以降、Win32でアトミックになりました:bugs.python.org/issue8828
OSレベルでの単一ファイルへのアクセスの調整には、おそらく解決したくないあらゆる種類の問題が伴います。
あなたの最善の策は、そのファイルへの読み取り/書き込みアクセスを調整する別のプロセスを持っていることです。
flock
。「独自のミューテックスとそれらを管理するデーモンプロセスをロールする」というアプローチは、解決するにはかなり極端で複雑なアプローチのように思われます。
ファイルのロックは通常、プラットフォーム固有の操作であるため、さまざまなオペレーティングシステムで実行できるようにする必要がある場合があります。例えば:
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')
そのため、古さを示し、状況を監視して問題を監視するために、その量の5倍から始めると考えました。
また、タイミングを調整しているときに、実際には必要のないコードが少しあることに気付きました。
lock.close()
これは、openステートメントの直後にあったため、この編集では削除しました。
追加するにはエヴァン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:
ブロック内のコードはスレッドセーフです。つまり、別のプロセスがファイルにアクセスする前にコードが終了します。
シナリオそのようなものです:ユーザーが何かをするファイルを要求します。次に、ユーザーが同じ要求を再度送信すると、最初の要求が完了するまで2番目の要求が行われないことをユーザーに通知します。そのため、この問題を処理するためにロックメカニズムを使用しています。
これが私の作業コードです:
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は非常に便利。これは、ファイルのロックまたは一般的なロックメカニズムに使用でき、一度に複数の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