Python デーモンスレッドの使い方

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

最近の若者は日本語がきちんとできていないということをよく耳にしますが、若者に限らず、文章を理解する力が下がっているようです。

まず、本を読む機会が少なくなって、人とコミュニケーションを取る機会も少なくなれば、当然日本語を話す理解するという機会が減ってるわけです。

わたしたちは小さな頃からの莫大な会話数によって、言語を習得しているわけなので、使わなくなれば、能力は下がる一方です。

作家やブロガーでもない限り、文章を書く機会も少ないので、読みやすく、わかりやすい文章を書こうと思っても経験がないからかけないわけです。

文章を書くからには、しっかりと読み返して、おかしなところがないかチェックしているのですが、たまにおかしな文章になっていることもあるので、改めて気をつけようと思います。

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

昨日の復習

昨日は、スレッドに渡す引数について学習しました。

Threadオブジェクトを生成する際に、いくつかの引数を指定することができましたが、targetには、関数を指定しましたが、加えてnameargskwargsを使ってコードを書きました。

今日は、その引数のdaemonについて学習します。

デーモンスレッド

デーモンというと、「デーモン小暮」しか思い浮かんでこないのですが、プログラミングの世界では、システムに常駐しているソフトのことで、なにかやらないといけないことができてきたときに処理をしてくれるらしいです。

PythonのThreadオブジェクトに用意されている引数のdaemonの場合で考えると、スレッドをデーモン化することで、デーモン化されたスレッド意外のスレッドの処理が終了したら、プログラムを終了するので、常駐しているソフトとは少し意味合いが違うような感じがします。

ただ、ネットにはバックグラウンドで作動しているプログラムという解説もあるので、処理の終了を意識する必要がないという定義で考えて、デーモンスレッドといわれているのかもしれません。

解説していてもよくわからないので、実際にコードを書いていきましょう。

thread.py

thread.pyは、昨日のコードから少し変更しています。

import logging
import threading
import time

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

def thread1():
    logging.debug('start1')
    time.sleep(1)
    logging.debug('end1')

def thread2():
    logging.debug('start2')
    time.sleep(5)
    logging.debug('end2')

if __name__ == '__main__':
    t1 = threading.Thread(target=thread1)
    t2 = threading.Thread(target=thread2)
    t2.setDaemon(True)
    t1.start()
    t2.start()
    print('started')

昨日のコードから変更している部分は、10行目と15行目のtime.sleepをそれぞれ15にして、19行目、20行目でThreadの引数をtargetだけにして、21行目にt2.setDaemon(True)を追加しました。

threadingモジュールを確認するとわかりますが、Threadオブジェクトの引数であるdaemonは、デフォルトでNoneになっています。

Python デーモンスレッドの使い方

スレッドをデーモン化するには、setDaemonを使ってdaemonTrueに指定します。

ちなみに、21行目を削除して、20行目を次のように変更しても同じようにデーモン化が可能です。

    t2 = threading.Thread(target=thread2, daemon=True)

コードが書けたら実行してみましょう。

実行結果

/Users/bigmacpro/opt/anaconda3/bin/python /Users/bigmacpro/PycharmProjects/thread/thread.py
started
Thread-1: start1
Thread-2: start2
Thread-1: end1

Process finished with exit code 0

スレッドが並列処理されているので、startedが最初に出力されています。

デーモン化していなければ、Thread-1: end1が出力された4秒後にThread-2: end2と表示されますが、デーモン化されたスレッド以外が終了しているため、Thread-1: end1が出力された時点でプログラムが終了します。

joinで待機

スレッドをデーモン化するということは、処理に時間がかかるスレッドを途中で投げ出す可能性があるということなので、デーモン化するスレッドは、データを取得して書き込むような処理は向いていません。

デーモン化したスレッドが終了するまでプログラムの終了を待たせるために使うのがjoinです。

    t2.join()

thread.pyのコードの最終行25行目に上記コードを追加します。

joinを使って、デーモン化したt2オブジェクトが終了するまでプログラムの終了を待機するようになります。

/Users/bigmacpro/opt/anaconda3/bin/python /Users/bigmacpro/PycharmProjects/thread/thread.py
Thread-1: start1
Thread-2: start2
started
Thread-1: end1
Thread-2: end2

Process finished with exit code 0

今回は、前回出力されなかったThread-2: end2が出力されます。

ちなみに、joinはデーモン化していないスレッドにも使えるので、明示的な意味からデーモン化していないt1も同様にt1.join()と記述するプログラマーもいるとのことです。

実践あるのみ

毎回言っているような気がしますが、大切なのは、やってみることです。

実際にコードを書いて、ちょっとづつコードを変更してみたりして、動作を確認したりするとより理解が深まります。

今回のデーモン化も基本的な使い方はわかっても、具体的にどんな場面で使うのかちょっとイメージができないので、いろいろと試しつつ実践に備えたいと思います。

それでは明日もGood Python!