これは、書き込みプロセスと読み込みプロセス間でも同様の問題が発生します。
こんなときに使うのがプロセス間の排他制御です。
今回紹介するのは、 fcntlモジュールです。 本モジュールはUnix系のシステムコールflock()を内部で呼ぶのでUnix限定となります。
基本的なロック・アンロックは以下のように実現します。
1 2 3 4 5 6 | import fcntl ifp = file("hoge.txt", "r") fcntl.flock(ifp.fileno(), fcntl.LOCK_EX) # 排他ロック獲得 # fcntl.flock(ifp.fileno(), fcntl.LOCK_SH) # 共有ロック獲得 fcntl.flock(ifp.fileno(), fcntl.LOCK_UN) # 排他ロック解放 ifp.close() |
ロックの種別は2つで、共有ロックと排他ロックがあります。
共有ロック(fcntl.LOCK_SH)は、複数プロセスでロックを獲得することができるもので、 主に読み込みしかしない処理区間で使用します。 実際、共有ロックは"r" or "r+"でオープンしたファイルディスクリプタに対してしか 使用できません。
大事なのは、共有ロック獲得中でも他プロセスは共有ロックを獲得できるが、 排他ロックとして獲得にきたプロセスはブロックされるということです。
つまり、誰か一人でも
このファイルをリードしている場合は、書き込み側である排他ロックを獲得しようとしているプロセスは
ブロックされるということです。
次は排他ロック(fcntl.LOCK_EX)です。
排他ロックはその名の通り、誰か一人でも排他ロックを獲得したら他プロセスからのロック
獲得はブロックされるというものです。このロックは、R/Wのどちらのモードでオープンしてもロックできます。
基本的には、書き込み区間で使用します。
上記のように読み込みプロセスと書き込みプロセスがいる場合、2つのロックを理解して使用しないと効率が
大変悪くなります。例えば、すべてのプロセスで排他ロックだけを使用していると、
読み込みだけの区間なのに、読み込みプロセスが排他ロックを獲得している間は、他の読み込みプロセスも
ブロックされることになります。
勉強不足がこのような非効率実装を生むことになるので、気をつけましょう。
ロックを解放するには、fcntl.LOCK_UNを使用します。
これは、共有ロック・排他ロックともに共通です。
最後に、上記のようなロックでブロックされる場合の挙動についてですが、
ロックを獲得できなかったプロセスは、上記実装ではスリープします。
つまり、プロセスの処理が停止して、他プロセスに処理が移ることを意味します。
その後誰かがロックを解放し順番がくるとスリープ状態から起こされ、ロックを獲得し、処理が継続します。
これをmutex lockと言います。
一方で、flockのフラグにfcntl.LOCK_NBを併用すると、
ロックが獲得できなかったときにスリープせずに処理を継続する、いわゆるtry lock
を実現できます。
違いは、ロックが獲得できなかったときのリトライ処理を自分で定義できるか否かです。
ファイルロックは複雑です。いろいろと調べて使ってみてください。