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

この間、かなり近くでトンビをみました。

最初はカラスかなと思っていたのですが、近づいてみるとくちばしがかっこよく曲がったトンビだったので、写真を撮ったのですが、近づきすぎたみたいですぐに飛んでいってしまいました。

とんび

10匹くらいいて、近づくと飛んでいくんですが、羽を広げるとかなり大きいのでびっくりしました。

トンビが鷹を生む」なんて言葉がありますが、個人的にはどっちがどっちやら見分けがつかないので、トンビと鷹でそんな差があるのかな?なんて思っちゃいます。

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

昨日の復習

昨日は、queueついて学習しました。

Queueにはファーストインファーストアウトとラストインファーストアウト、優先順位付きの3つの種類がありました。

リストのように値を入れて、入れた順番に値を出していくのがファーストインファーストアウトで、スレッドに値を入れて、ちがうスレッドで入れた順番に出力していくことができました。

並列処理になるので、値を入れると同時に出力されますが、出力する方は、値が入力されていないからといって、スレッドを終了することなく、値が入力されるのを待機して、入力されてから出力処理を行っていました。

詳細については、昨日の記事をごらんください。

今日は、キュー(queue)のもう少し高度な使い方を学習します。

Whileループを使う

昨日は、一つのスレッドに値を入れて別のスレッドでその値を取り出しましたが、今回は、メインのスレッドで10個値を入れてオブジェクト化したスレッドで、whileループを使って取り出してみます。

import logging
import queue
import threading

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

def thread1(queue):
    logging.debug('start1')
    while True:
        item = queue.get()
        if item is None:
            break
        logging.debug(item)
    logging.debug('end1')

if __name__ == '__main__':
    queue = queue.Queue()
    for i in range(10):
        queue.put(i)
    t1 = threading.Thread(target=thread1, args=(queue, ))
    t1.start()
    queue.put(None)

9行目から13行目でwhileループを使って、18行目、19行目でqueueに入れた値を取り出しています

whileループを終了するのは、queueNoneが入っていた場合にして、最後の22行目でqueueNoneを代入しています。

このコードの実行結果は次のとおりです。

Thread-1: start1
Thread-1: 0
Thread-1: 1
Thread-1: 2
Thread-1: 3
Thread-1: 4
Thread-1: 5
Thread-1: 6
Thread-1: 7
Thread-1: 8
Thread-1: 9
Thread-1: end1

最初と最後には、start1end1が出力されています。

間には、forループで代入したqueueの値をwhileループで取り出しているのがわかります。

ちなみに最後の22行目のqueue.put(None)の記述がないと9行目のqueue.get()で値の入力を待った状態になるので、いつまで立ってもスレッドが終了しません

queueのtask_doneとjoin

非常に長い処理をqueueの処理が終了したあとにさせたい場合は、queueの終了を待つコードを入れなければなりません。

def thread1(queue):
    logging.debug('start1')
    while True:
        item = queue.get()
        if item is None:
            break
        logging.debug(item)
        queue.task_done()
        
    logging.debug('very long work')
    logging.debug('end1')

if __name__ == '__main__':
    queue = queue.Queue()
    for i in range(10):
        queue.put(i)
    t1 = threading.Thread(target=thread1, args=(queue, ))
    t1.start()
    logging.debug('tasks are still working')
    queue.join()
    logging.debug('tasks are finished')
    queue.put(None)

    t1.join()

17行目の処理が非常に時間がかかるので、whileループの処理が必ず終了してから行う場合は、スレッドでも使うjoinを使います

queuejoinを使う場合は、15行目のtask_doneを使用します。

これでqueueに入れた値がすべて使われて、28行目のtasks are finishedが出力されたあと、再びqueueNoneが代入されて、10行目のwhileループがようやく終了するという感じです。

ちなみに31行目のt1.join()は、不要なのですが、明示的に記述しています。

実行結果

Thread-1: start1
MainThread: tasks are still working
Thread-1: 0
Thread-1: 1
Thread-1: 2
Thread-1: 3
Thread-1: 4
Thread-1: 5
Thread-1: 6
Thread-1: 7
Thread-1: 8
Thread-1: 9
MainThread: tasks are finished
Thread-1: very long work
Thread-1: end1

この結果はきれいに順番がわかりやすいように出力されていますが、2行目のMainThread: tasks are still workingは、出力の順番が変わることがあります。

MainThreadの処理が終了したあと、処理に時間がかかるコードが読み込まれて最終end1が出力されています。

単体では理解できない

処理が複雑でいまいち理解できていないかと思います。

私自身頭が整理できておらず、なんとなくこんな感じなのかな程度しか理解できていないので、理解するまでにもう少し時間が必要です。
今回学習した内容だけで理解するのではなく、これまでに学習してきた内容を踏まえた上であれば理解できるようになるもの早いのかなと思います。

とにかく同じようなコードを繰り返し反復練習することが重要なので、コードをコメントアウトしたり、追加したりしていろいろと試してみてください。

わからなかったことがふとした拍子に理解できるようになるかもしれませんよ。

それでは明日も、Good Python!