blog

日常・技術のことを記録する

python2系で排他ロックする方法

pythonで書き込み・読み込み中のファイルにwriteをぶつけるとファイルが壊れる(?)らしい。 DBとか使えば勝手に排他かけてくれるらしいけど、まあpythonだけでやるならどうしましょって話。

・解 fcntl.flock関数を使う

・参考 公式リファレンス fcntl --- fcntl および ioctl システムコール — Python 3.7.4 ドキュメント

・注意点 ロック獲得に失敗した場合に排出されるエラーが、 2系と3系で違うので注意(IOErrorとOSError)。

下記は2系

#!/usr/bin/python
# -*- coding: utf-8 -*-
#読み込み("r")の共有ロックスクリプト

import fcntl
import inspect
import time

max_retry_count = 3
pouse_second = 1

try:
    with open("/home/foo/bat.txt", 'r') as fileobj:
        for i in range(max_retry_count):
            try:
                # ロック獲得(共有ロック)
                fcntl.flock(fileobj.fileno(),
                            fcntl.LOCK_SH | fcntl.LOCK_NB)
            except IOError as ioerror:
                "/home/foo/record.log".write('{n}:{f}:{m}'.format(
                                    n=__name__,
                                    f=inspect.currentframe().f_code.co_name,
                                    m=ioerror.args))
                if i + 1 == max_retry_count:
                    raise
                # ロック獲得失敗
                time.sleep(pouse_second)
                continue

            try:
                # ロック獲得成功 
                wifi_ap_id = fileobj.read()
                break
            except Exception as e:
                # 異常発生
                raise ValueError(e)
            finally:
                fcntl.flock(fileobj.fileno(), fcntl.LOCK_UN)
except Exception as e:
    "/home/foo/record.log".write('{n}:{f}:{m}'.format(
                                    n=__name__,
                                    f=inspect.currentframe().f_code.co_name,
                                    m=e.args))
#!/usr/bin/python
# -*- coding: utf-8 -*-
#書き込み("w")の専有ロックスクリプト
import fcntl
import inspect
import time

max_retry_count = 3
pouse_second = 1

    try:
        filepath = "/home/foo.kakikomisaki.txt"
        jstr = "kakikomitaimoji"
        if os.path.isfile(filepath):
            option = "r+"
        else:
            option = "w"

        with open(filepath, option) as fileobj:
            for i in range(max_retry_count):
                try:
                    # ロック獲得
                    fcntl.flock(fileobj.fileno(),
                                fcntl.LOCK_EX | fcntl.LOCK_NB)
                except IOError as ioerror:
                    "/home/foo/record.log".write('{n}:{f}:{m}'.format(
                        n=__name__,
                        f=inspect.currentframe().f_code.co_name,
                        m=ioerror.args))
                    if i + 1 == max_retry_count:
                        raise
                    # ロック獲得失敗
                    time.sleep(pouse_second)
                    continue

                try:
                    # ロック獲得成功 ボディ部保存
                    fileobj.truncate()
                    fileobj.write(jstr)
                    break
                except Exception as e:
                    # 異常発生
                    raise ValueError(e)
                finally:
                    fcntl.flock(fileobj.fileno(), fcntl.LOCK_UN)
    except Exception as _exception:
        "/home/foo/record.log".write('{n}:{f}:{m}'.format(
                                    n=__name__,
                                    f=inspect.currentframe().f_code.co_name,
                                    m=_exception.args))

差分もまるまる書いちゃった\(^o^)/ コピペでスクリプトとして動かせる~わーい