【完全独学Python】threadingで効率的な並行処理(マルチスレッド)を実現する!

Tips

こんにちは!ゆーや(@yuyamanm118712)です。

こちらのページでは完全独学でWebプログラマとなった私が

効率的な並行処理を実装する方法をご紹介します!

難しそうに感じるかもしれませんが、実際に動いているものを見れば

絶対に理解できます!

最後までじっくりと読んで、一緒に学習にしていきましょう!

なお、ご指摘・ご質問はTwitter(@yuyamanm118712)のDMまでよろしくお願いします!

この記事を読むと

  • Pythonでの並行処理(マルチスレッド)の実装の仕方がわかる!
  • threadingの基本的な使い方について理解できる!
  • threadingを使ったサンプルコードが手に入る!

並行処理

並行処理とは

並行処理とは、複数のタスクが同時に実行されることを指します!

これは、プログラムで言うと「プログラム内の複数の部分が同時に実行されること」になります!

実装方法としては「マルチスレッド」や「マルチプロセス」などの方法がありますが、

今回は「マルチスレッド」での実装を考えていきます!

ゆーや
ゆーや

ちなみにPythonでマルチプロセスを実装する方法もあるから

以下の記事を見てみてね!

※記事執筆中…少々お待ちください。

マルチスレッドとは

1つのプロセス内で複数のスレッドを実行する方法です!

各スレッドは、プロセス内の同じメモリ空間を共有しますが、個別のスレッドスタックを持ちます!

これにより、スレッド間でデータを共有しながら並行処理を実現することができます!

ゆーや
ゆーや

実際に動作するのを見れば、納得できる!

threadingとは

Pythonで並行処理を行う際に非常に役立つのがthreadingモジュールです!

このモジュールを使用することで、複数のスレッドを用いて同時に複数のタスクを実行することができます!

この記事では、threadingモジュール基本的な使い方から、オプションやメソッドの詳細までを紹介します。

始めての方でも理解できるように、各セクションにサンプルコードと説明を付けていますので

ぜひ、最後までご覧ください!

threadingモジュールの基本

スレッドの作成と開始

import threading
import time

# スレッドで実行する関数
def print_numbers():
    for i in range(5):
        print("thread:" + str(i))
        time.sleep(1)  # 1秒待機

# スレッドの作成
thread = threading.Thread(target=print_numbers)

# スレッドの開始
thread.start()

# メインスレッドでの処理
for i in range(5, 10):
    print("main:" + str(i))
    time.sleep(1)
ゆーや
ゆーや

無事動いたかな?画像のような表示が出ればOK!
mainのスレッド自分で作ったthread同時に動いているのがわかるね!

スレッドの実行が完了するまで待機(join)

import threading
import time

def print_numbers():
    for i in range(5):
        print("thread:" + str(i))
        time.sleep(1)

thread = threading.Thread(target=print_numbers)
thread.start()

# スレッドの終了を待機
thread.join()

print("main:" + "スレッドが終了しました")
ゆーや
ゆーや

threadingjoinメソッドを使うとスレッドの処理が終わるまで

メインの実行を待機させることができます!

threadingモジュールのオプションとメソッド

スレッドの識別(get_ident())

import threading
import time

def print_thread_id():
    print(f"スレッドID: {threading.get_ident()}")
    

thread = threading.Thread(target=print_thread_id)
thread.start()

thread2 = threading.Thread(target=print_thread_id)
thread2.start()
ゆーや
ゆーや

get_ident()スレッドが持つ独自のIDを取得できる!
これで1つのプロセス内でも各スレッドを識別できるね!

スレッドの名前設定(name)

import threading
import time

def print_thread_name():
    print(f"スレッド名: {threading.current_thread().name}")

thread = threading.Thread(target=print_thread_name, name="MyThread")
thread.start()
thread.join()

thread2 = threading.Thread(target=print_thread_name, name="MyThread2")
thread2.start()
thread2.join()
ゆーや
ゆーや

Thread作成時にnameオプションで好きな名前を設定できる!
これで名前での識別も可能!

デーモンスレッド(setDaemon(True))

import threading
import time

def background_task():
    while True:
        print("バックグラウンドタスク実行中")
        time.sleep(2)

# デーモンスレッドの作成
thread = threading.Thread(target=background_task)
thread.setDaemon(True)
thread.start()

# メインスレッドの処理
time.sleep(5)
print("メインスレッド終了")
ゆーや
ゆーや

デーモンスレッドは、メインスレッドが終了すると自動的に終了するスレッド
分かりにくい人はsetDaemonの行を消してみよう!
ただし、無限ループになるので取扱注意!

ロック(Lock)

import threading

# 共有変数
counter = 0

# ロックオブジェクトの作成
lock = threading.Lock()

def increment():
    global counter
    for _ in range(1000000):
        # ロックを取得
        lock.acquire()
        counter += 1
        # ロックを解放
        lock.release()

def decrement():
    global counter
    for _ in range(1000000):
        # ロックを取得
        lock.acquire()
        counter -= 1
        # ロックを解放
        lock.release()

# スレッドの作成
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=decrement)

# スレッドの開始
thread1.start()
thread2.start()

# スレッドの終了を待機
thread1.join()
thread2.join()

# カウンターの値を表示
print("Counter:", counter)
ゆーや
ゆーや

counterという変数を「thread1」と「thread2」が同時にアクセスしてしまう状況だけど、
きちんとロックを管理することで、競合を避け、正しい結果が求められているね!

スレッド間の通信や同期(Condition)

import threading

condition = threading.Condition()
shared_data = []

def consumer():
    with condition:
        condition.wait()  # データが追加されるまで待機
        print(f"消費者がデータを受け取りました: {shared_data}")

def producer():
    with condition:
        shared_data.append(1)
        print("生産者がデータを追加しました")
        condition.notify()  # 待機中のスレッドを通知

# スレッドの作成
consumer_thread = threading.Thread(target=consumer)
producer_thread = threading.Thread(target=producer)

# スレッドの開始
consumer_thread.start()
producer_thread.start()

# スレッドの終了を待機
consumer_thread.join()
producer_thread.join()
ゆーや
ゆーや

condition.wait()で他のスレッドの合図を待機し、condition.notify()で待機している
2つのスレッド間でshared_dataというリストをうまく共有できる!

まとめ

本記事では、threadingを使って、Pythonで並列処理(マルチスレッド)を実装する方法を紹介しました!

ポイントは以下の6つです!

① Pythonで並列処理(マルチスレッド)を実装するには「threading」を使う
② 「threading.Thread」メソッドは、スレッドの作成
③ 「start」メソッドは、スレッドの開始
④ 「join」メソッドは、スレッドの実行が完了するまで待機
⑤ 「get_ident」メソッドは、スレッドの識別
⑥ 「name」オプションは、スレッドの名前設定
⑦ 「setDaemon(True)」メソッドは、デーモンスレッドの設定
⑧ 「Lock」メソッドは、競合を防ぐためロック
⑨ 「Condition」メソッドは、スレッド間の通信や同期

ゆーや
ゆーや

まとめの内容を見て、もう頭に浮かべば完璧!
今日もお疲れ様です!

コメント

タイトルとURLをコピーしました