並列処理プログラミングのメモ#4 (Reader / Writer)
| 登録日 | :2025/05/03 09:35 |
|---|---|
| カテゴリ | :Python基礎 |
同期を設計するもう一つのよく知られた方法。リーダー/ライター問題
読み取りは、複数のプロセス(スレッド)から可能な状態とする一方で、書込みを行う場合は、排他的にする必要がある(書込みの場合は、他からの読み込み・書込み禁止。逆に読み込みの中は、書込みは禁止)
ファイルサーバ上のファイルの読み書きをイメージするとわかりやすい。
- 任意の数のリーダーが共有データの読み取りを同時に行うことができる。
- 共有データに書込みを行うことができるライターは一度に一つだけ。
- ライターが共有データに書込みを行っている間、リーダーが共有データを読み取ることはできない。
このようにして、読み取り/書き込みエラーや、書き込み/書き込みエラーによる競合状態や不適切なインタリーブを防ぐ。
ライブラリやプログラミング言語には、こうした問題解決のために「読み取り/書き込みロック」(RWLock)が含まれていることがよくある。PythonにはそのようなLockがないので、RWLockを実装してから実現するサンプルコード。
rwlock.py
"""
Chapter 9 /reader_writer/rwlock.py
"""
from threading import Lock
class RWLock(object):
def __init__(self) -> None:
self.readers = 0
self.read_lock = Lock()
self.write_lock = Lock()
def acquire_read(self) -> None:
self.read_lock.acquire()
self.readers += 1
if self.readers == 1:
self.write_lock.acquire()
self.read_lock.release()
def release_read(self) -> None:
assert self.readers >= 1
self.read_lock.acquire()
self.readers -= 1
if self.readers == 0:
self.write_lock.release()
self.read_lock.release()
def acquire_write(self) -> None:
self.write_lock.acquire()
def release_write(self) -> None:
self.write_lock.release()
RWLockを用いて実行する、reader_writet.py
"""
Chapter 9/reader_writer/reader_writer.py
"""
import time
import random
from threading import Thread
from rwlock import RWLock
# share resource
counter = 0
# mutex
lock = RWLock()
class User(Thread):
def __init__(self, idx: int):
super(User, self).__init__()
self.idx = idx
def run(self) -> None:
while True:
lock.acquire_read()
print(f"User {self.idx} reading: {counter}")
# time.sleep(random.randrange(1, 3))
time.sleep(1)
lock.release_read()
time.sleep(1)
class Librarian(Thread):
def run(self) -> None:
# change global parameter: share resource
global counter
while True:
lock.acquire_write()
print("Librarian waiting ...")
counter += 1
print(f"New value: {counter}")
time.sleep(random.randrange(1, 3))
lock.release_write()
if __name__ == "__main__":
threads = [
User(0),
User(1),
Librarian()
]
for thread in threads:
thread.start()
for thread in threads:
thread.join()