Pythonでファイルのロック@Windows|Linux

ファイルのロックがね。WindowsとLinuxと違うわけですが。

基本的にはPythonではOSの違いなんかはほぼ考慮しないで大丈夫な感じなわけですが、
ファイルの排他ロックなんかだけは、共通に書けないのは、まぁしかたないことです。

ということで、マスタ的にjsonファイルを扱ったりして、PHPでもC++でもPythonでもアクセスするような感じにするので、やっぱり、ファイルのロックとかはどうしても必要になります。

しかし、なんだりかんだりで、Python@Windowsの場合のファイルロックが、なんかマニュアル見てもよくわかんなかったりしたので、それを検証してみましょう。

Windowsの場合のファイルロックはどうやるの?

LINUXとかの場合には、

fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)

とか書けばよいのですが、Windowsの場合には、どうするかっていうと、こんな風にかくようです。

msvcrt.locking(fh.fileno(), msvcrt.LK_NBLCK,	500000000)

ファイルをOpenするモード

実は、これにちょっと戸惑ってしまいまして。
要するに、WindowsのようにファイルのOpenモードと、ロックするモードを同時に指定できないため、わずかな時間差を考慮し始めるとメンドイと思ったわけです。

ということで、いろいろやってみた結果、

rb+しかない

という結論です。要するに、
seekしてからwriteするには、これ以外は思ったようにいかなかったからなのです。
まあwriteモードは、最初に切り詰めちゃいますからそうなんですが、

ab+とかやっても、先頭にseekしてからwriteしても、末尾に追加されてしまう

という結果だったということです。
よって、基本、rb+でOpenすることを基本にしたクラスを作ってしまいます。

そして、lockするところで例外を拾って排他処理をかけましょ。

あと、、すみません。テキストモードとか使ったことないのです。
どこで使うかほぼ意味がわかりませんし。。

しかし、、、lockingの時のサイズがよくわからんなぁーー^^;

作ってみた、第一弾

import os
import msvcrt
#-----------------------------------------
if os.name != 'nt':
  import fcntl
#-----------------------------------------
class  hiraFile:
  #---------------------------------------
  def __init__(self):
    self.f      = None
    self.fname    = ""
    self.mode    = ""
    self.maxloop  = 50
    self.locksize  = 1024
  #---------------------------------------
  def __enter__(self):
    #print("前処理")
    return self
  #--------------------------
  def __exit__(self, exc_type, exc_value, traceback):
    #print("後処理")
    a = 0
  #--------------------------
  def __del__(self):
    if self.f != None:
      self.f.close()
    self.f      = None
    self.fname    = ""
  #--------------------------
  def isLinux(self):
    return  os.name != 'nt'
  def isWindows(self):
    return  os.name == 'nt'
  #--------------------------
  #  open mode:r,w,aで受け付ける
  #--------------------------
  def open(self,fname,mode="r"):
    try:
      self.f = open(fname,"rb+")
      try:
        if(mode != "r"):
          if(self.isWindows()):
            msvcrt.locking(self.f.fileno(), msvcrt.LK_NBLCK,  self.locksize)
          else:
            fcntl.flock(self.f, fcntl.LOCK_EX | fcntl.LOCK_NB)
        if(mode == "a"):
          self.f.seek(0,2)  #LAST
        else:
          self.f.seek(0)    #TOP
        return  True
      except Exception as e:
        self.f.close()
        self.f = None
    except Exception as e:
      self.f = None
    return  False
  #--------------------------
  def close(self):
    if self.f:
      if(self.isWindows()):
        msvcrt.locking(self.f.fileno(), msvcrt.LK_UNLCK,  self.locksize)
      else:
        fcntl.flock(self.f, fcntl.LOCK_UN)
      self.f.close()
      self.f = None
  #---------------------------------------
  def write(self,dat,n=0):
    try:
      if(n == 0):
        v = self.f.write(dat.encode("utf-8"))
      else:
        v = self.f.write(dat,n)
    except Exception as e:
      v = False
    return  v
  #--------------------------
  def read(self,n=0):
    try:
      if(n == 0):
        v = self.f.read()
      else:
        v = self.f.read(n)
    except Exception as e:
      v = False
    return  v
  #--------------------------
  def seek(self,a,b):
    try:
      v = self.f.seek(a,b)
    except Exception as e:
      v = False
    return  v
  #--------------------------
  def truncate(self,a):
    try:
      v = self.f.truncate(a)
    except Exception as e:
      v = False
    return  v
  #--------------------------
  def getPos(self):
    try:
      v = self.f.tell()
    except Exception as e:
      v = False
    return  v
  #--------------------------
#--------------------------
if __name__ == '__main__':
  a = hiraFile()
  a.open("rlock.dat")
  v = a.read(15)
  print("read-v:",v)
  v = a.getPos()
  print("getPos-v:",v)

まぁこんなとこかな。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です