Python - マルチスレッディング

こんにちは、未来のPythonの魔法使いたち!今日、私たちはPythonのマルチスレッディングの魔法の世界に興味深い旅に出ます。プログラミングが初めての方でも心配しないでください。あなたの友好的なガイドとして、私はこのトピックをステップバイステップに探求します。だから、あなたの仮想の杖(キーボード)を取って、一緒に飛び込みましょう!

Python - Multithreading

マルチスレッディングとは?

Pythonのスレッドで魔法の呪文を唱える前に、まずマルチスレッディングが何であるかを理解しましょう。あなたが忙しい厨房で料理をしているシェフだと想像してみてください。一人で料理をしている場合、一度に一つのタスクしかできません - 野菜を切る、それから水を沸騰させ、それから肉を焼く。しかし、異なるタスクを同時に行える複数の手があったらどうでしょうか?それがマルチスレッディングが私たちのプログラムに行うことです!

マルチスレッディングは、プログラムが単一のプロセス内で複数のタスクを同時に実行することができます。それは、複数のシェフ(スレッド)が同じ厨房(プロセス)で協力して美味しい料理(プログラムの出力)をより早く効率的に作るように見えます。

プロセスとの比較

今、あなたは「でも先生、プロセスも聞いたことがあります。スレッドとはどのように異なるのでしょうか?」と思うかもしれません。素晴らしい質問です!それを解説しましょう:

  1. リソース使用量:スレッドは部屋(メモリ空間)を共有する兄弟のように、プロセスは別の家を持つ隣人のように。スレッドは軽量でリソースを共有するため、特定のタスクにおいてより効率的です。

  2. 通信:スレッドは変数を共有することで簡単に話し合うことができますが、プロセスはお互いに話すために特別な「電話」(インタープロセス通信)を使用する必要があります。

  3. オーバーヘッド:スレッドの作成と管理は通常、プロセスに比べてより速く、システムリソースを必要としません。

  4. 複雑性:スレッドはプログラムを加速させることができますが、複雑性を導入します。それはジャグリングのように、正しく行えば楽しいものの、注意しないとボールを落としてしまうかもしれません!

Pythonのスレッドハンドリングモジュール

Pythonは寛大な言語ですが、スレッドを操作するための複数のモジュールを提供しています。主要なのは以下の2つです:

  1. threading:これはスレッドを操作するための高レベルインターフェースです。それは、あなたに重い仕事を大部分をやってくれる友好的な魔法の見習いのようです。

  2. _thread:これは低レベルインターフェースです。それは、正しく使用するためには多くの知識が必要な古い呪文の本のようです。

今日の魔法の旅では、より初心者に優しいであり広く使用されているthreadingモジュールに焦点を当てます。

新しいスレッドの開始

さて、まず最初のスレッドの呪文を唱えましょう!以下が新しいスレッドを作成して開始する方法です:

import threading
import time

def print_numbers():
for i in range(5):
time.sleep(1)
print(f"Thread 1: {i}")

# 新しいスレッドを作成
thread1 = threading.Thread(target=print_numbers)

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

# メインスレッドは実行を続ける
for i in range(5):
time.sleep(1)
print(f"Main thread: {i}")

# thread1が終了するのを待つ
thread1.join()

print("All done!")

この魔法の呪文を分解してみましょう:

  1. threadingtimeモジュールをインポートします。
  2. スレッドが実行する関数print_numbers()を定義します。
  3. 新しいスレッドオブジェクトを作成し、実行する関数を指定します。
  4. start()メソッドを使用してスレッドを開始します。
  5. メインスレッドは自身のループを実行し続けます。
  6. プログラムを終了する前にjoin()を使用して、スレッドが完了するのを待ちます。

これを実行すると、スレッド間で数値が交錯して表示され、同時実行が示されます!

スレッドの同期

今、私たちのシェフの助手が同じナイフを同時に使おうとする姿を想像してみてください。大変ですよね?これがスレッドの同期が登場する場面です。私たちは、一度に一つのスレッドしかアクセスできない共有リソースに対してロックを使用して、同期を行います。

以下がその例です:

import threading
import time

# 共有リソース
counter = 0
lock = threading.Lock()

def increment_counter():
global counter
for _ in range(100000):
lock.acquire()
counter += 1
lock.release()

# 2つのスレッドを作成
thread1 = threading.Thread(target=increment_counter)
thread2 = threading.Thread(target=increment_counter)

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

# 両方のスレッドが完了するのを待つ
thread1.join()
thread2.join()

print(f"Final counter value: {counter}")

この例では、ロックを使用して一度に一つのスレッドしかカウンターをインクリメントできないようにし、競合状態を防ぎます。

マルチスレッド優先度キュー

最後に、マルチスレッディングの実践的な応用例として、優先度キューを見てみましょう。病院の救急治療室で、患者が到着時間だけでなく、症状の重症度に基づいて治療されるように。

import threading
import queue
import time
import random

# 優先度キューを作成
task_queue = queue.PriorityQueue()

def worker():
while True:
priority, task = task_queue.get()
print(f"Processing task: {task} (Priority: {priority})")
time.sleep(random.uniform(0.1, 0.5))  # 作業をシミュレート
task_queue.task_done()

# ワーカースレッドを作成して開始
for _ in range(3):
thread = threading.Thread(target=worker, daemon=True)
thread.start()

# キューにタスクを追加
for i in range(10):
priority = random.randint(1, 5)
task = f"Task {i}"
task_queue.put((priority, task))

# すべてのタスクが完了するのを待つ
task_queue.join()
print("All tasks completed!")

この例では、複数のスレッドが優先度キューからタスクを効率的に処理する方法を示しています。

結論

おめでとうございます、若いPythonistaたち!あなたたちは、マルチスレッディングの魔法の世界に初めての一歩を踏み出しました。覚えておいてください、大きな力は大きな責任と共に来ます。スレッドを賢く使用することで、プログラムはより速く、より効率的になります。

以下は、今日カバーした主要なスレッドメソッドの簡易リファレンスです:

メソッド 説明
Thread(target=function) 指定された関数を実行する新しいスレッドを作成
start() スレッドのアクティビティを開始
join() スレッドが完了するのを待つ
Lock() スレッド同期用のロックを作成
acquire() ロックを獲得
release() ロックを解放

継続的に練習し、興味深いことを探求し続けることで、まもなくPythonの名匠となるでしょう!ハッピーコーディング!

Credits: Image by storyset