0Pricing
Python Academy · レッスン

スレッドとGIL

Pythonのスレッド処理の制限を理解します

「スレッドとGIL」はCoddyKit上の無料Python Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPython Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Python Academyコースには全4レッスンが含まれています。

スレッドとは

スレッドは、1つのプロセス内にある独立した実行の流れです。Pythonのthreadingモジュールを使うと、1つのプログラム内で関数を並行して実行できます。

import threading

def worker():
    print('hello from thread')

t = threading.Thread(target=worker)
t.start()
t.join()
print('main done')

複数のスレッドを開始する

複数のスレッドを起動できます。それぞれが対象の関数を実行します。join()を使うと、メインスレッドはスレッドが終了するまで待機します。

import threading

def task(name):
    print('task', name)

threads = [threading.Thread(target=task, args=(i,)) for i in range(3)]
for t in threads:
    t.start()
for t in threads:
    t.join()
print('all finished')

グローバルインタープリターロック

CPythonにはグローバルインタープリターロック(GIL)があります。Pythonバイトコードを実行できるスレッドは、一度に1つだけです。そのため、純粋なPythonによるCPU処理では、スレッドを使っても真の並列化による高速化は得られません。

import sys
print('Running on:', sys.implementation.name)
print('Only one thread runs Python bytecode at a time due to the GIL')

それでもスレッドが役立つ場面

スレッドはI/Oバウンドな処理で力を発揮します。ネットワーク要求、ディスク読み取り、待機などです。1つのスレッドが待機している間はGILが解放され、別のスレッドを実行できます。

import threading, time

def fake_io(name):
    time.sleep(0.1)
    print('done', name)

start = time.perf_counter()
ts = [threading.Thread(target=fake_io, args=(i,)) for i in range(3)]
for t in ts: t.start()
for t in ts: t.join()
print('elapsed about 0.1s, not 0.3s')

CPUバウンド処理は高速化されない

重い計算処理では、GILによってスレッドの実行が直列化されるため、スレッドを追加しても高速化されません。CPU処理には、代わりにmultiprocessingを使用します。

import threading

def burn():
    total = 0
    for i in range(1000000):
        total += i
    return total

t = threading.Thread(target=burn)
t.start()
t.join()
print('CPU-bound: threads share one core under the GIL')

デーモンスレッド

デーモンスレッドは、メインプログラムが終了すると自動的に終了します。終了を待つ必要のないバックグラウンド処理に使用します。

import threading, time

def background():
    time.sleep(0.05)
    print('background tick')

t = threading.Thread(target=background, daemon=True)
t.start()
t.join()
print('main exits')

スレッドの名前付けと識別

各スレッドには名前とIDがあります。threading.current_thread()は実行中のスレッドオブジェクトを返すため、ログ出力に便利です。

import threading

def show():
    t = threading.current_thread()
    print('running in', t.name)

t = threading.Thread(target=show, name='Worker-1')
t.start()
t.join()
print('main thread:', threading.current_thread().name)

Threadのサブクラス化

対象関数を渡す代わりに、Threadをサブクラス化してrun()をオーバーライドすることもできます。スレッドが独自の状態を持つ必要がある場合に便利です。

import threading

class Greeter(threading.Thread):
    def __init__(self, name):
        super().__init__()
        self.who = name
    def run(self):
        print('hi', self.who)

g = Greeter('Sam')
g.start()
g.join()

スレッドから結果を返す

対象関数の戻り値は破棄されます。結果を収集するには、共有構造に書き込みます(後で、キューなどのより安全なツールを学びます)。

import threading

results = {}

def compute(key):
    results[key] = key * key

ts = [threading.Thread(target=compute, args=(i,)) for i in range(4)]
for t in ts: t.start()
for t in ts: t.join()
print(results)

アクティブなスレッド数を数える

threading.active_count()は、メインスレッドを含む、現在生存しているスレッドの数を返します。

import threading, time

def wait():
    time.sleep(0.05)

ts = [threading.Thread(target=wait) for _ in range(2)]
for t in ts: t.start()
print('active now:', threading.active_count())
for t in ts: t.join()
print('active after join:', threading.active_count())

スレッドとプロセスの選び方

原則として、I/Oバウンドな並行処理にはスレッドを、CPUバウンドな並列処理にはプロセスを使用します。その理由はGILにあります。

tasks = {'download files': 'threads', 'crunch numbers': 'processes', 'call APIs': 'threads'}
for task, choice in tasks.items():
    print(task, '->', choice)

理解度チェック

スレッドとGILの理解度を確認しましょう。

まとめ

スレッドとGILについて学びました。

  • スレッドはthreading.Threadを通じて、1つのプロセス内で並行して実行されます。
  • GILはPythonバイトコードの実行を直列化します。
  • スレッドはI/Oバウンドな処理に、プロセスはCPUバウンドな処理に役立ちます。
  • start()、join()、デーモンスレッド、サブクラス化を使用します。

次は、ThreadPoolExecutorを使ってスレッドを適切に管理します。

よくある質問

「スレッドとGIL」レッスンは無料ですか?

はい。「スレッドとGIL」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Python Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Python Academyコースには全4レッスンが含まれています。

「スレッドとGIL」で何を学びますか?

Pythonのスレッド処理の制限を理解します ブラウザで直接実行するハンズオンコードでPython Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Python Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPython Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「スレッドとGIL」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPython Academyレッスンでコードを書いて実行できますか?

はい。すべてのPython Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. スレッドとGIL
  2. ThreadPoolExecutor
  3. multiprocessingの基礎
  4. データの安全な共有
← Python Academyに戻る