質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

Q&A

解決済

1回答

4302閲覧

python2.7で正確に「ファイルの排他ロックをする方法」が知りたい

umino

総合スコア54

Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

0グッド

0クリップ

投稿2019/06/26 02:11

python2.7で「ファイルの排他ロックをする方法」について知識のある方、
教えていただければ助かります。よろしくおねがいします。

前提・実現したいこと

pythonでファイル削除処理(os.remove)に対して排他ロックをかける方法を知りたい。

2つのpythonスクリプトを使用して、
1つのテキストファイル(locktest.txt)に対して排他ロックをかけ、
書き込み→ファイル削除を行いたいです。

発生している問題・エラーメッセージ

os.remove("/filepath")に排他ロックをかける方法がわからない。

使用するスクリプトは下記の3つです。
①writeするためのpythonスクリプト(書き込み中は排他ロックを20秒ほどかける設定、正常動作している)
②Deleteするためのpythonスクリプト(ここがわからないポイント)
③write,deleteされる対象のテキストファイル(locktest.txt)

③に対し、①でwriteする際に排他ロックをかけているのですが、
①を実行している最中に②のファイル削除を実行すると、排他が効かず③が削除されてしまいます。
そのため、①のアンロック時に③がみつからず、
下記のようなエラーが発生します。

$ python aaa_write_first.py ロックを獲得できるまで待ちます ロックを獲得できました ------------------ ロックを獲得できた時の処理をここに書きます 例えば、ここでは 20秒間スリープします Traceback (most recent call last): File "aaa_write_first.py", line 24, in <module> main() File "aaa_write_first.py", line 20, in main fcntl.flock(f.fileno(), fcntl.LOCK_UN) ValueError: I/O operation on closed file

該当のソースコード

↓①write用ファイル(書き込み後、20秒排他ロック)

python

1# -*- coding: utf-8 -*- 2 3import fcntl 4 5def main(): 6 fname = '/home/foo/bar/locktest.txt' 7 8 try: 9 with open(fname, "w") as f: 10 print('ロックを獲得できるまで待ちます') 11 fcntl.flock(f.fileno(), fcntl.LOCK_EX) 12 print('ロックを獲得できました') 13 f.write('first' * 500) 14 print('------------------') 15 print('ロックを獲得できた時の処理をここに書きます') 16 print('例えば、ここでは 20秒間スリープします') 17 import time 18 time.sleep(20) 19 finally: 20 fcntl.flock(f.fileno(), fcntl.LOCK_UN)

↓①delete用ファイル(①のwriteファイル実行中に実行するためもの)
こちらのos.removeプロセスに排他ロックをかける方法がわからず、知識のある方に教えていただきたいです。

python

1# -*- coding: utf-8 -*- 2 3import fcntl 4 5def main(): 6 fname = '/home/foo/bar/locktest.txt' 7 8 try: 9 print('ファイル削除を開始します') 10 import os 11 os.remove(fname) 12 # writeと同じように排他ロックをかけようとするとエラーになる 13 # fcntl.flock(os.remove(fname.fileno()), fcntl.LOCK_EX) 14 print('ファイル削除を完了しました') 15 except Exception as e: 16 print(e) 17 finally: 18 pass

試したこと

ファイル削除処理に排他ロックをかけるために、fcntlライブラリのflockメソッドを使用したがエラーになった。

python

1fcntl.flock(os.remove(fname.fileno()), fcntl.LOCK_EX) 2 3>>出力結果 4'str' object has no attribute 'fileno' 5

補足情報

python2.7

知識のある方、教えていただければ助かります。よろしくおねがいします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

'str' object has no attribute 'fileno'

エラーメッセージをよく読んでください。 str 型の値には fileno という属性が無いですよ、と書いてあります。
fname.fileno() でミスってませんか?


追記(2019-06-26)

Traceback (most recent call last): File "aaa_write_first.py", line 24, in <module> main() File "aaa_write_first.py", line 20, in main fcntl.flock(f.fileno(), fcntl.LOCK_UN) ValueError: I/O operation on closed file

こちらのエラーについては with でファイルを開いており、そのスコープ外で f.fileno() を参照しようとしているのでファイルが閉じられている、というのが原因かと思います。
以下のような方法だと別プロセスからファイルが削除されていたとしてもアンロックでエラーは発生しませんでした。(それはそれでどうなの、というのもありますが)

python

1import fcntl 2import time 3 4try: 5 f = open("sample.txt", "w") 6 print("sample.txt opened.") 7 fcntl.flock(f.fileno(), fcntl.LOCK_EX) 8 print("sample.txt locked.") 9 time.sleep(60) 10 11finally: 12 fcntl.flock(f.fileno(), fcntl.LOCK_UN) 13 print("sample.txt unlocked.") 14 f.close() 15 print("sample.txt closed.")

ファイル削除の方も一旦開いて、削除してからクローズ、という形で問題ありません。

python

1import fcntl 2import os 3 4try: 5 f = open("sample.txt", "r") 6 print("sample.txt opened.") 7 fcntl.flock(f.fileno(), fcntl.LOCK_EX) 8 print("sample.txt locked.") 9 os.remove("sample.txt") 10 print("sample.txt removed.") 11finally: 12 fcntl.flock(f.fileno(), fcntl.LOCK_UN) 13 print("sample.txt unlocked.") 14 f.close() 15 print("sample.txt closed.")

投稿2019/06/26 02:22

編集2019/06/26 05:15
mather

総合スコア6753

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

umino

2019/06/26 02:28

回答ありがとうございます。 エラー文の内容は把握しているのですが、解消方法がわかりません。 ①のopen-writeの実装では、同じ書き方でエラーがでませんでした。 同じ実装でエラーが排出されるということは、 「①open,"w"したファイルに対しては排他ロックをかけられるが、 ②os.removeしたいファイルに対しては排他ロックがかけられない」のかと考えています。 私の理解が誤っている可能性が高いので、そこも含めて、エラー解消方法を知りたい状態です。
mather

2019/06/26 02:32

いや、ご自身で f.fileno() としたときのコードをみると fname を open してからFileオブジェクトに対して fileno を取得してますよね。 一方でエラーが発生した方は fname に対して直接 fileno を取得しようとしています。 同じ fileno を取得するコードを比較してみればわかることですよ。
umino

2019/06/26 02:54

なるほど、比較してみました。 削除メソッドに排他ロックをかけるためには、 write側でいうopenのような動作をかませてからfileオブジェクトに対してfilenoを取得すればよいのでしょうか。removeにopenをかませてみたのですが(当たり前かもしれませんが)エラーになり、考え方も含め、具体的にどのように実装すれば解決できるのかが詰まってしまっている状態です。
mather

2019/06/26 03:04

エラーの解消の話ではないんですか? どうやって書けばいいですか、というのは丸投げです。 考え方の話でいうと、「ファイルの削除」というのは「ファイル内容の変更」なので書き込みロックで十分です。
mather

2019/06/26 03:14

いや、違いますね。 「削除されるのを防ぎたい側」にとってのロックはありますが、そもそも削除する側はロック不要ですね。
umino

2019/06/26 04:15

本来の目的は「pythonでファイル削除処理(os.remove)に対して排他ロックをかける方法を知りたい」です。 投稿には書きましたが、「削除されるのを防ぎたい側(write-①)」にとってのロックは正常にかかりました。これはwrite-writeのプロセス競合で確認済みです。 しかし、write-deleteとなると、「削除する側(delete-②)」のプロセスがwriteの排他をを突破してファイルを削除してしまいます。exceptionは投稿の通りです。 たしかに、「削除する側はロック不要」の可能性もありますが、事実として「削除される側でロックをかけているにも関わらず突破して削除されて」います。上記のwrite-writeのケースから鑑みるに、削除する側でもロックをかければ想定した動きになると想像しています。
mather

2019/06/26 05:19

追記しました。「試したこと」のエラーの解消だと勘違いしていました。 「発生しているエラー」の部分に関してはそもそもファイル削除をしなくても発生するかと思います。 ここでいう排他ロックに関してはアドバイザリーロックなので、「ロックを取得しに行く」という操作をしないと強制力はありません。 つまり、Pythonとは無関係に rm コマンドなどで削除されることも普通にありますし、防ぐことはできません。
umino

2019/06/27 01:09

返信おそくなりすみません、きのう、頂いた回答をもとに実装してみたら無事動きました! おっしゃる通り、withのスコープ外でunlockをしようとしていたことが原因でした。 ご丁寧にコードまでいただいて、イメージでき派生した不明点も調べやすかったです。 本当にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問