Python学習【365日チャレンジ!】209日目のマスターU(@Udemy11)です。
家の中を整理したので、かなりの不用品が出てきました。
全く使っていない新品の食器などがたくさん出てきたので、メルカリで売ることにしたのですが、ちょっとお得な価格で出品するとほんとあっという間に売れちゃうんです。
コマーシャルでも売れている商品の90%は出品から24時間以内に売れているなんてことを言っていたので、ほんとめちゃくちゃたくさんの人がメルカリをチェックしてるんでしょうね。
ちなみによく売れている商品だったとしても値段がちょっと高いと売れないんです。
それでは、今日もPython学習をはじめましょう。
昨日の復習
昨日は、スレッドのTimer
オブジェクトについて学習しました。
スレッドの処理を後回しにしたいときに使うのがTimer
オブジェクトでした。
args
やkwargs
の引数をターゲットの関数に引き渡すこともできましたね。
詳細については、昨日の記事をごらんください
今日は、同時進行の処理を受け付けなくするLockを学習します。
thread.py
今日は、Lockの学習をしますが、そのまえに、どのような場面で使うかを理解しておきましょう。
次のような、辞書型の値を使うスレッドについて考えます。
import logging
import threading
logging.basicConfig(level=logging.DEBUG, format='%(threadName)s: %(message)s')
def thread1(d):
logging.debug('start1')
i = d['x']
d['x'] = i + 1
logging.debug(d)
logging.debug('end1')
def thread2(d):
logging.debug('start2')
i = d['x']
d['x'] = i + 1
logging.debug(d)
logging.debug('end2')
if __name__ == '__main__':
d = {'x': 1}
t1 = threading.Thread(target=thread1, args=(d, ))
t2 = threading.Thread(target=thread2, args=(d, ))
t1.start()
t2.start()
ハイライトしている行が変更したところですが、辞書型の引数d
を取るスレッドを2つ用意して、同じ計算をさせています。
このコードを実行すると、次のようになります。
実行結果
Thread-1: start1
Thread-1: {'x': 2}
Thread-1: end1
Thread-2: start2
Thread-2: {'x': 3}
Thread-2: end2
thread2
では、d
に1
が代入されるのではなく、thread1
の計算結果である2
が代入されて計算されています。
これは、並列処理から考えると少し変な感じがしますが、上から順番に処理されるルールになっているのかなとも思います。
次に、11行目にtime.sleep(1)
を挿入して、thread1
の処理を1秒間遅らせるとどうなるでしょう。
実行結果は次のようになります。
実行結果
Thread-1: start1
Thread-1: {'x': 2}
Thread-1: end1
Thread-2: start2
Thread-2: {'x': 2}
Thread-2: end2
この場合は、thread2
のd
に1が代入されて上書きされるので、x
が同じ2になってしまいます。
ただ、thread2
の18行目にtime.sleep(1)
を代入した場合は、最初の出力結果と同じになるんですよね。
なぜ同じタイムリープを入れてもちがう結果になるのかはちょっと理解できていないのですが、この対処法として、Lock
を使う方法があります。
requireとrelease
インポートなどの行は省いて8行目から記述しますが、ハイライトしている行が変更部分です。
def thread1(d, lock):
logging.debug('start1')
lock.acquire()
i = d['x']
time.sleep(1)
d['x'] = i + 1
logging.debug(d)
lock.release()
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()
29行目でLock
オブジェクトを作成し、8行目、18行目の関数と30行目・31行目のオブジェクトに引数のlock
を入れて、それぞれの関数の計算が始まる前にlock.require()
と出力が終わったあとにlock.release()
を挿入しています。
これで実行結果は最初と同じようになります。
実行結果
Thread-1: start1
Thread-2: start2
Thread-1: {'x': 2}
Thread-1: end1
Thread-2: {'x': 3}
Thread-2: end2
バグの発見につながる
今日学習したコードは、処理方法やタイミングによって実行結果が違ってくるので、バグにつながるコードにも関わらず、普通に問題なく出力していると問題に気づくことがありません。
このようなバグを見つけ出すためにテストを行っているわけなので、同じ処理なのに実行結果が違ってくる場合は、原因究明が必要です。
地道な作業がプログラムのスキルを向上させる近道ではあるので、いろいろとコードを試すようにしましょう。
それでは明日もGood Python!