Pythonのthreading.enumerateの使い方

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

ふと気づけば庭の木の枝から大部分の葉っぱがなくなっているのに気づきました。

よくよくみてみると、たくさんのミノムシの簑がぶら下がっているので、どうやらミノムシが葉っぱを食い散らかしていたみたいです。

以前もみじを植えていたのですが、こちらはカミキリムシにやられて枯れてしまったので、同じように枯れてしまわないよう、駆除しておきました。

といってもぶら下がっている枝を切り落としただけなので、地面からまた這い上がってくるかもしれませんが。。。

害虫予防は重要ですね。

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

昨日の復習

昨日は、デーモンスレッドについて学習しました。

プログラミングにおけるデーモンは悪魔のことではなく、常駐ソフトのことですが、Pythonのdaemonはちょっと意味合いが違いました。

threadをデーモン化することで、デーモン化したthreadが処理を完了するのを待たずにプログラムを終了させることができました。

逆にデーモン化したthreadの終了を待つための関数がjoinでした。

今日は、生存中!?のスレッドを返すenumerateについて学習します。

forループ

threadはforループを使って複数のthreadを立ち上げることができます。

import logging
import threading
import time

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

def thread():
    logging.debug('start')
    time.sleep(1)
    logging.debug('end')

if __name__ == '__main__':
    threads = []
    for _ in range(3):
        t = threading.Thread(target=thread)
        t.start()
        threads.append(t)

14行目で空のリストthreadsを定義し、forループを使って3つのスレッドをリストに入れて実行します。

実行結果

Thread-1: start
Thread-2: start
Thread-3: start
Thread-1: end
Thread-3: end
Thread-2: end

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

17行目にt.setDaemon(True)を挿入すると、上記実行結果の3行目までの出力でプログラムが終了します。

joinを使って最後までスレッドを実行しようと最後の行にt.join()を入れて実行すると、実行結果は次のようになります。

実行結果

Thread-1: start
Thread-1: end
Thread-2: start
Thread-2: end
Thread-3: start
Thread-3: end

1行目の出力のあと、1秒後に2行目と3行目が出力されて、1秒後に4行目と5行目、さらに1秒後に6行目が出力されます。

これでは並列処理にはなりませんね。

threadsをループしてjoin

forループでデーモン化した複数のスレッドを同時進行するには、スレッドを入れたリストthreadsforループで回してjoinすれば並列処理することができます。

if __name__ == '__main__':
    threads = []
    for i in range(3):
        t = threading.Thread(target=thread)
        t.setDaemon(True)
        t.start()
        threads.append(t)
    for thread in threads:
        thread.join()

ハイライトした20行目と21行目で作成したスレッドをjoinしています。

このコードを実行すれば、最初の実行結果と同じになります。

enumerate

enumerateを使えば、リストthreadsを作らなくてもここまでのコードと同じ処理をすることができます。

if __name__ == '__main__':
    for i in range(3):
        t = threading.Thread(target=thread)
        t.setDaemon(True)
        t.start()
    for thread in threading.enumerate():
        if thread is threading.currentThread():
            print(thread)
            continue
        thread.join()

enumerateを使えば、空のリストを定義して代入する必要がなくなります。

ただ、enumerateは、生存中(進行中)のスレッドを返すので、自動的に生成されるメインスレッドオブジェクト(MainThread)も含まれます。

19行目から21行目はthreadMainThreadだった場合は、threadを出力して、joinしないというコードなので、「不要なのでは?」なんて思い、抜いて実行してみました。

すると下記のようなエラーが。

Pythonのthreading.enumerateの使い方

カレントスレッドはjoinできないようですね。

なので、19行目から21行目でMainThread(カレントスレッド)をjoinから除外しているわけです。

実行結果

Thread-1: start
Thread-2: start
Thread-3: start
<_MainThread(MainThread, started 4671282624)>
Thread-2: end
Thread-3: end
Thread-1: end

4行目でカレントスレッドが出力されたあと、1秒待って残りの3行が出力されます。

味わい深い

このあたりのレクチャーも、酒井さんの講座ではサラッと解説されています。

ここまで学習している人であれば、カレントスレッドがjoinできないことは前提条件として理解している体で進むので、理解できないことがあれば、すぐにネットで調べたほうがいいでしょう。

とにかくわからないことは自分で調べて、いろいろと試してみることが重要です。

それでもわからないときは、直接質問することができるので、最終的に解決することができます。

ほんと酒井さんのPython講座は内容が充実しているので、Pythonの基礎からひととおり学習したい人や復習したい人には超おすすめの講座ですので、ぜひUdemyのセール時に受講してみてください。

Udemy講師 酒井さん プログラミング Python

それでは、明日もGood Python!