PYTHON MEBY

PythonでGIL(グローバルインタプリタロック)を理解する

この記事では、Pythonのグローバルインタプリタロック(GIL)について、その仕組み、影響、そして回避策を解説します。マルチスレッドプログラミングにおけるGILの制約と、それを考慮したコーディング手法を学びます。

目次

GILとは何か?

Pythonのグローバルインタプリタロック(GIL)とは、CPythonインタプリタにおけるロック機構で、一度に1つのスレッドだけがPythonバイトコードを実行できるように制限するものです。

これは、Pythonのメモリ管理モデルと密接に関連しており、スレッドセーフなメモリ管理を実現するために導入されました。

GILは、CPython固有のものであり、JythonやIronPythonなどの他のPython実装には存在しません。

GILの影響

GILは、マルチスレッドプログラムのパフォーマンスに影響を与えます。特にCPUバウンドなタスクでは、マルチスレッド化による速度向上は期待できません。

複数のCPUコアがあっても、GILによって1つのコアしか同時にPythonコードを実行できないため、並列処理が実現できません。

I/Oバウンドなタスク(ネットワークアクセスやディスクI/Oなど)では、GILの影響は小さくなります。スレッドがI/O操作待ちになっている間、他のスレッドが実行できるためです。

マルチスレッドとGIL

Pythonでマルチスレッドプログラミングを行う場合、GILの影響を考慮する必要があります。

import threading
import time

def task():
    for _ in range(5):
        print(f"Thread {threading.current_thread().name}: Working...")
        time.sleep(1)

threads = []
for i in range(3):
    thread = threading.Thread(target=task, name=f"Thread-{i}")
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

上記の例では、3つのスレッドが順番に実行されます。実際には同時に実行されません。

GILを回避する

GILによる制限を回避するには、マルチプロセッシングを使用します。

マルチプロセッシングでは、複数のプロセスがそれぞれ独立したインタプリタとメモリ空間を持つため、GILの影響を受けません。

マルチプロセッシング

マルチプロセッシングは、`multiprocessing` モジュールを使用します。

import multiprocessing
import time

def task():
    for _ in range(5):
        print(f"Process {multiprocessing.current_process().name}: Working...")
        time.sleep(1)

if __name__ == '__main__':
    processes = []
    for i in range(3):
        process = multiprocessing.Process(target=task, name=f"Process-{i}")
        processes.append(process)
        process.start()

    for process in processes:
        process.join()

この例では、3つのプロセスが並列に実行されます。

マルチプロセッシングは、プロセス間通信のコストを考慮する必要があります。プロセス間でデータのやり取りをする場合は、`Queue` や `Pipe` などの機構を使う必要があります。

まとめ

PythonのGILは、マルチスレッドプログラミングにおける制約となりますが、マルチプロセッシングによって回避できます。CPUバウンドなタスクのパフォーマンス向上にはマルチプロセッシングが有効です。I/Oバウンドなタスクであれば、マルチスレッドでも効果があります。

関連記事