Python

【Python】マルチスレッドの使い方【Thread】

投稿日:2021年4月23日 更新日:

Pythonには、マルチスレッドと呼ばれるテクニックがあります。
一般的にプログラム言語は上から下に順番に1行ずつ実行されるものです。
Pythonでは、それをスレッドとして複数同時に取り扱うことが可能です。
このテクニックをマルチスレッドと呼びます。
マルチスレッドを利用することで、全体の処理時間を短縮することができます。

今回は、そのマルチスレッドの使い方を解説します。

マルチスレッドとは?

通常、Pythonで実行している処理の流れは、上から順番に1行ずつ実行されます。
したがって、途中で何か重い処理があると、その行で処理が終了するまで、全体の処理の流れが止まってしまいます。

そこで、マルチスレッドが登場します。
1行ずつ実行される処理をスレッドとして、複数のスレッドを同時に実行することをマルチスレッドと呼びます。
これを活用することによって、重い処理はスレッドを分けて実行することで、全体の処理時間を短縮することができます。

Pythonでは、main関数から実行されるスレッドをマルチスレッドと呼びます。
mainスレッドから派生して作成・実行されたメソッドを単にスレッドと呼びます。

マルチスレッドは、全体の処理時間を短縮することができますが、デメリットもあります。
それは、処理の順番に整合性がなくなることです。
複数のスレッドで同じクラスや変数に対して処理を加える場合は、十分に注意する必要があります。

使い方

使い方はとても簡単です。
以下のソースコードを例として、解説します。

import threading
import time

def process():
    print('Process Start')
    time.sleep(3)
    print('Process End')

print('Main Start')

threadA = threading.Thread(target=process)
threadA.start()

time.sleep(1)

print('Main End')

1行目で、マルチスレッドに必要なモジュールをimportしてます。
threadingは、特にpip insallなどは不要で、標準で使用することができます。

2行目の、timeモジュールは、マルチスレッドとは特に関係しません。
6行目で、一定時間処理を止めるため、time.sleep()が使用したいので、importしました。

4~7行目はスレッドで実行する用の関数です。
time.sleep()を何らかの重たい処理と捉えてください。

11行目で、threadAと言うスレッドを定義しています。
threadAは、process()を実行するスレッドとなりました。
このように、スレッドの定義は、関数に対しておこないます。

12行目で、threadAを開始しました。
定義した通りに関数が実行されます。

さて、このソースコードの実行結果は以下の通りです。

Main Start
Process Start
Main End
Process End

11,12行目でスレッドを使ったので、process()の出力が後ろにきました。
また、実行時間もsleepは合計で4秒ありますが、3秒で終わりました。

11,12行目を、単にprocess()に置き換えた場合(つまり、マルチスレッド化しない場合)の実行結果は以下の通りです。
こちらの場合は、4秒かかりました。

Main Start
Process Start
Process End
Main End

追記:
上記の例では、threadAの終了を待たずにメインスレッドを終了させてしまっています。
実はこれは良くないことですので、下記補足のテクニックを使って、スレッドがmainスレッドよりも早く終わらないようにするか、デーモン化してください。

補足

join()を使ったスレッドの終了方法

上記の例では、メインのスレッドが先に終わってしまいました。
threadAとメインの終了を同時にしたい場合は、以下のようにjoinメソッドを使います。

import threading
import time

def process():
    print('Process Start')
    time.sleep(3)
    print('Process End')


print('Main Start')

threadA = threading.Thread(target=process)
threadA.start()

print('Main Process')
time.sleep(1)

threadA.join()

print('Main End')
Main Start
Main Process
Process Start
Process End
Main End

join()を使うことで、メインのスレッドは18行目で処理を停止して、threadAの終了を待ちます。

処理をどこかのタイミングで同期させたい時などに有効です。

デーモンを使ったスレッドの終了方法

上記の例では、メインスレッドがthreadAよりも早く終わってしまいました。
メインスレッド終了後も、threadAが生き残り、処理を続けることとなりました。

作ったスレッドをメインスレッドの終了とともに、終了させる方法があります。
それは、スレッドをデーモン化することです。

以下がスレッドのデーモン化の例です。

import threading
import time

def process():
    print('Process Start')
    time.sleep(3)
    print('Process End')


print('Main Start')

threadA = threading.Thread(target=process)
threadA.setDaemon(True)
threadA.start()

time.sleep(1)

print('Main End')
Main Start
Process Start
Main End

メインスレッドの終了を持って、threadAも終了したので、’Process End’は出力されませんでした。

非常に重たい処理があるときに、有効なテクニックです。

Python学習におすすめの教材

Pythonについて、しっかりと基礎から学びたい、実践的なスキルを身につけたいと考えている方向けに、おすすめの学習教材を紹介します。
これらの教材は、実際に僕が使った教材になっています。

現役シリコンバレーエンジニアが教えるPython 3 入門 + 応用 +アメリカのシリコンバレー流コードスタイル

現役シリコンバレーエンジニアが教えるPython入門!応用では、データ解析、データーベース、ネットワーク、暗号化、並列化、テスト、インフラ自動化、キューイングシステム、非同期処理など盛り沢山の内容です!

ベネッセが運営する動画学習サイトUdemyで視聴することができる動画教材です。
(セールの時には¥1,500程度で買えます!)

現役シリコンバレーエンジニアが教えるPython 3 入門 + 応用 +アメリカのシリコンバレー流コードスタイル

エキスパートPythonプログラミング改訂2版 Pythonプログラミングのベストプラクティスを伝授

本書は、Pythonを使って仕事をしている開発者が普段どのようなツールやテクニックを用いて仕事をしているのか、また開発者が実際に現場で用いているベストプラクティスについて解説した書籍です。本書を読むことで、先進的なPythonプログラマが日常的に使用している開発ノウハウを学ぶことができます。

より実務に近いノウハウが盛りだくさんの書籍です。
Pythonエンジニアを目指す方は持っておいて損はないと思います。

-Python
-,

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

【入門】Pythonの学習で失敗しないためにおすすめの教材

Pythonを学習するうえで、慎重に選びたいのが学習教材です。Pythonは手軽に始められるがゆえに、我流でやってしまうと変な癖がついてしまいやすく、結局実用的なスキルにならないとうことが多々発生しま …

【Kivy】ボタンのフィードバックと重い処理を分ける方法【Python】

Kivyでアプリ開発をしていると、MVCのように処理と表示を分けたくなることがあると思います。その時、ボタンをトリガーにすると、ボタンのフィードバックのタイミングが処理の終了タイミングと同じになってし …

【Python】pytestの使い方【テスト駆動開発】

今回は、テスト駆動開発の足掛けとして、pytestの使い方を紹介します。 テスト駆動開発とは、テストのことを第一に考えて実装する開発法のことです。アジャイル開発のように、設計・開発・テストを短く繰り返 …

【Python】loggingの正しい使い方

開発効率やメンテナンス性の観点から、ログを残すことは非常に重要です。 Pythonにはログ出力用に「logging」モジュールが用意されています。loggingを使うことで、簡単にログ管理をすることが …

【Python】グローバル変数が更新されない時の対処法

Pythonでグローバル変数を取り扱っているとき、関数内でグローバル変数の値を更新しても、更新が反映されていないということはないでしょうか。 これには、Pythonのお作法が関わっています。この解決法 …

yassanさんのプロフィール写真

yassan
インフラエンジニア。
独学でインフラ・クラウドについて学んでいます。
大阪 / 20代後半 / 決闘者 / FE / AWS-SAA

twitter: @yassan10787859