Pythonでスレッドによる並行処理を実装する方法を徹底解説
はじめに
Pythonには、スレッド、サブプロセス、ジェネレータなど、並行プログラミングを実現するためのさまざまな手法が用意されています。本記事では、その中でも最も基本的な「スレッド」を使った並行処理の実装方法を解説します。実装に入る前に、まず「並行性(Concurrency)」とは何かを理解しておきましょう。
並行性とは、1つのプログラム内に存在するロジックが、複数の独立した実行パスを開き、それぞれが同時にかつ互いに独立して動作しているように見える状態を指します。例えば、個別のI/Oストリームの処理やSQLクエリの実行などがこれに該当します。
シングルスレッドでの実装
まずは比較のため、単一スレッドで複数のWebサイトURLに順番にアクセスするシンプルなプログラムを作成してみましょう。
# Step 1 - アクセスしたいサイトのURLリストを作成 import requests tutorialpoints_url = ['https://www.tutorialspoint.com/python/index.htm', 'https://www.tutorialspoint.com/cplusplus/index.htm', 'https://www.tutorialspoint.com/java/index.htm', 'https://www.tutorialspoint.com/html/index.htm', 'https://www.tutorialspoint.com/cprogramming/index.htm']
次に、渡されたURLにリクエストを送り、ステータスコードを返す関数を定義します。
# URLにリクエストを送り、ステータスコードを表示する関数
def visit_site(site_url):
"""
指定されたURLにGETリクエストを送り、レスポンス情報を出力する
"""
response = requests.get(site_url)
print(f' *** {site_url} returned {response.status_code} after {response.elapsed} seconds')メイン処理では、URLリストをループして各サイトに順次アクセスします。
# シングルスレッドでレスポンスを取得する
if __name__ == '__main__':
for site_url in tutorialpoints_url:
visit_site(site_url)
print(f" *** end of the program ***")実行結果は以下のようになります。
*** https://www.tutorialspoint.com/python/index.htm returned 200 after 0:00:00.091103 seconds *** https://www.tutorialspoint.com/cplusplus/index.htm returned 200 after 0:00:00.069889 seconds *** https://www.tutorialspoint.com/java/index.htm returned 200 after 0:00:00.075864 seconds *** https://www.tutorialspoint.com/html/index.htm returned 200 after 0:00:00.075270 seconds *** https://www.tutorialspoint.com/cprogramming/index.htm returned 200 after 0:00:00.077984 seconds *** end of the program ***
出力結果から何がわかるでしょうか? URLが逐次的(順番に)処理されていることが確認できます。もし地理的に分散した数百件のURLにアクセスしたい場合、サーバーからの応答待ちに膨大な時間を費やすことになってしまいます。
マルチスレッドによる並行処理への書き換え
そこで、リクエストを並列に送信し、応答を待たずに次の処理へ進むスレッド版プログラムを書いてみましょう。threadingモジュールのThreadクラスを使用します。
from threading import Thread
# URLにリクエストを送り、ステータスコードを表示する関数
def visit_site(site_url):
"""
指定されたURLにGETリクエストを送り、レスポンス情報を出力する
"""
response = requests.get(site_url)
print(f' *** {site_url} returned {response.status_code} after {response.elapsed} seconds')
# 各URLに対してスレッドを生成して実行
if __name__ == '__main__':
for site_url in tutorialpoints_url:
t = Thread(target=visit_site, args=(site_url,))
t.start()実行結果を見てみましょう。
*** https://www.tutorialspoint.com/python/index.htm returned 200 after 0:00:00.082176 seconds *** https://www.tutorialspoint.com/html/index.htm returned 200 after 0:00:00.086269 seconds *** https://www.tutorialspoint.com/java/index.htm returned 200 after 0:00:00.100746 seconds *** https://www.tutorialspoint.com/cplusplus/index.htm returned 200 after 0:00:00.120744 seconds *** https://www.tutorialspoint.com/cprogramming/index.htm returned 200 after 0:00:00.111489 seconds
今回はURLリストの順序どおりに出力されておらず、各スレッドが同時に(並行して)実行されていることがわかります。これにより、全体の処理時間を大幅に短縮できます。
解説:スレッドの仕組みと状態確認
- threadingライブラリを使うと、任意のPython呼び出し可能オブジェクト(関数など)を独自のスレッド上で実行できます。
- start()メソッドを呼び出すと、指定した引数(site_url)とともに
visit_site関数が起動されます。 - 開始されたスレッドは、オペレーティングシステムによって完全に管理されながら、それぞれ独立したスレッドとして実行されます。
さらに、スレッドが実行中か完了済みかを確認したい場合は、is_alive()関数を使用します。
if t.is_alive():
print(f' *** {t} is Still executing')
else:
print(f' *** {t} is Completed')実行結果の例:
*** <Thread(Thread-10, stopped 4820)> is Completed
このように、Pythonのthreadingモジュールを活用すれば、I/O待ちが発生する処理(Webアクセス、ファイル読み書き、DBクエリなど)を効率的に並行化できます。ただし、CPUバウンドな処理の場合はGIL(Global Interpreter Lock)の影響を受けるため、multiprocessingモジュールやconcurrent.futuresの活用も検討するとよいでしょう。
-
PythonのBokehで画像を操作・表示する方法
Pythonの可視化ライブラリBokehで画像を扱うには、image_url()メソッドを使い、画像URLのリストを渡すのが基本です。このメソッドを使うことで、ローカルやWeb上にある画像をプロット領域内に簡単に配置できます。実装の手順output_file()でデフォルトの出力先を設定し、show()を呼び出したときに結果がHTMLファイルとして保存されるようにします。figure()でプロット用の新しいFigure(図)オブジェクトを作成します。image_url()メソッドで、指定したURLから読み込んだ画像を描画します。show()を呼び出して、Bokehオブジェクトやアプリケーション
-
PythonでAPIの結果を可視化する方法|GitHub APIとPlotlyを使った実践チュートリアル
はじめにAPIを活用する最大のメリットの一つは、常に最新のライブデータを取得できる点です。データが頻繁に更新される場合でも、API経由なら最新の状態を確実に取得できます。APIプログラムは、特定の情報をリクエストするために専用のURLを使用します。たとえば「SpotifyやYouTube Musicで2020年に最も再生された楽曲トップ100」といった具合です。リクエストされたデータは、JSONやCSVなど、プログラムで扱いやすい形式で返却されます。Pythonを使えば、ほぼあらゆるURLに対してAPI呼び出しを記述できます。本記事では、GitHubのAPIからデータを抽出し、その結果をグラフ