スレッドと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フィードバックを取得できます。ローカル設定は不要です。