PythonのスレッドをロックするRLock

Python学習【365日チャレンジ!】210日目のマスターU(@Udemy11)です。

テレビをつけながらブログを書いているとついついドラマに見入ってしまいます。

特に、半沢直樹は現在の水戸黄門のような勧善懲悪ドラマなので、結論がわかっているのにハラハラドキドキしながらみてしまいます。

とはいえ、ブログに本腰を入れられないので、テレビから離れて作業したほうがいいかもしれません。

それでは、今日もPython学習をはじめましょう。

昨日の復習

昨日は、スレッドのLockについて学習しました。

スレッドは並列処理ができるものの、同じ変数の計算をしてしまうと代入に関してバグが起こる可能性がありましたが、thread.Lockを使えば、そのバグを回避することができました。

具体的な使用シーンはまだまだイメージできないのですが。。。

今日は、Lockに関係するRLockを学習します。

withを使ってthread

thread.Lockでオブジェクトを作って、acquirereleaseで他のスレッドの処理を止めることができましたが、にwithステートメントを使って同じような処理をさせることができます。

import logging
import threading
import time


logging.basicConfig(level=logging.DEBUG, format='%(threadName)s: %(message)s')


def thread1(d, lock):
    logging.debug('start1')
    with.lock:
        i = d['x']
        time.sleep(1)
        d['x'] = i + 1
        logging.debug(d)
        logging.debug('end1')


def thread2(d, lock):
    logging.debug('start2')
    lock.acquire()
    i = d['x']
    d['x'] = i + 1
    logging.debug(d)
    lock.release()
    logging.debug('end2')


if __name__ == '__main__':
    d = {'x': 1}
    lock = threading.Lock()
    t1 = threading.Thread(target=thread1, args=(d, lock))
    t2 = threading.Thread(target=thread2, args=(d, lock))
    t1.start()
    t2.start()

ハイライトしている部分がwithステートメントを使ったlockの場合、自動的に開いて閉じるので、lock.releaseは必要ありません。

実行結果

Thread-1: start1
Thread-2: start2
Thread-1: {'x': 2}
Thread-1: end1
Thread-2: {'x': 3}
Thread-2: end2

2行目までが出力されたあと、1秒後に残りの行が出力されます。

thread1thread2はコードが違いますが、同じ意味合いになります。

with内with

次に、withステートメントの中にさらにwithステートメントを使ったlockを入れるとどうなるでしょう?

def thread1(d, lock):
    logging.debug('start1')
    with.lock:
        i = d['x']
        time.sleep(1)
        d['x'] = i + 1
        logging.debug(d)
        with lock:
            d['x'] = i + 1
    logging.debug('end1')

withステートメントを入れ子にすると、16行目まで処理がされたあと、lock.releaseされていない状態でwith lockが来るので、ずっとlock.releaseを待った状態になってしまいます。

実行結果

Thread-1: start1
Thread-2: start2
Thread-1: {'x': 2}

プログラムが終了しない状態になるので、この状態を回避するためにRLockを使います。

Rlock

先程のコードからlock = threading.Lock()lock = threading.RLock()に変更するだけです。

    lock = threading.RLock()

RLockを使うことで、releaseされていない状態のコード内でlockを使うことができ、内部のlockreleaseしたあと、外部のlockreleaseしてくれます。

わからないこともある

わからないことはわかるまでやってみることが大切だといっていますが、考えてもわからないときは一旦離れてみることも重要です。

離れてみてちがうことを学習したあとに、復習してみると、意外と簡単に理解できることがあります。

他の知識を吸収することでそれまでは理解できなかったことがわかるようになっているわけです。

すぐにわからないと諦めるのではなく、しっかりと実践して最終的に理解できなかったことは、一旦忘れてしまってみてください。

ふとした時に道がひらけたように理解できることもありますから。

それでは明日もGood Python!