中学理科1626207 views
英語607877 views
小学算数1194618 views
いろは2986023 views
高校生物549842 views
小学社会308636 views
LaTeX957300 views
Computer365120 views
高校化学2913383 views
小学理科717236 views
Help
Tools

English

Python の GIL とは

GIL(Global Interpreter Lock)は、Python インタプリタが一度に1つのスレッドだけが Python バイトコードを実行することを保証するロック機構です。マルチスレッドプログラミングを理解する上で、GIL の存在は非常に重要です。

GIL とは何か

GIL は CPython(標準の Python 実装)に存在するグローバルなロックです。このロックにより、複数のスレッドがあっても、実際に Python コードを実行できるのは常に1つのスレッドだけです。

import threading
import time

counter = 0

def increment():
    global counter
    for _ in range(1000000):
        counter += 1

t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)

start = time.time()
t1.start()
t2.start()
t1.join()
t2.join()

print(f"結果: {counter}")  # 2000000 にならないことがある
print(f"時間: {time.time() - start:.2f}秒")

なぜ GIL が存在するのか

GIL は Python のメモリ管理を単純化するために導入されました。

参照カウント

Python はオブジェクトの参照カウントでメモリを管理している。GIL がないと、複数スレッドが同時に参照カウントを変更してメモリ破損が起きる可能性がある。

C 拡張の安全性

多くの C 拡張モジュールは GIL を前提に書かれている。GIL があることで、スレッドセーフでない C コードも安全に呼び出せる。

GIL の影響

CPU バウンド処理

複数スレッドでも並列実行されない。マルチスレッドの恩恵を受けにくい。

I/O バウンド処理

I/O 待ち中は GIL が解放される。マルチスレッドの恩恵を受けられる。

CPU バウンドな処理では、マルチスレッドにしても速くならないことがあります。

import threading
import time

def cpu_bound():
    total = 0
    for i in range(10000000):
        total += i
    return total

# シングルスレッド
start = time.time()
cpu_bound()
cpu_bound()
print(f"シングルスレッド: {time.time() - start:.2f}秒")

# マルチスレッド
start = time.time()
t1 = threading.Thread(target=cpu_bound)
t2 = threading.Thread(target=cpu_bound)
t1.start()
t2.start()
t1.join()
t2.join()
print(f"マルチスレッド: {time.time() - start:.2f}秒")
# ほぼ同じか、むしろ遅いことがある

I/O 処理では GIL が解放される

ファイル I/O やネットワーク通信などの I/O 操作中は、GIL が一時的に解放されます。そのため、I/O バウンドな処理ではマルチスレッドが効果的です。

import threading
import time

def io_bound():
    time.sleep(1)  # I/O をシミュレート

# シングルスレッド
start = time.time()
io_bound()
io_bound()
print(f"シングルスレッド: {time.time() - start:.2f}秒")  # 約2秒

# マルチスレッド
start = time.time()
t1 = threading.Thread(target=io_bound)
t2 = threading.Thread(target=io_bound)
t1.start()
t2.start()
t1.join()
t2.join()
print(f"マルチスレッド: {time.time() - start:.2f}秒")  # 約1秒

GIL を回避する方法

CPU バウンドな処理を並列化したい場合の選択肢があります。

multiprocessing

プロセスごとに独立した GIL を持つため、真の並列実行が可能。

C 拡張 / NumPy

NumPy などの C 拡張は、計算中に GIL を解放することがある。

別の Python 実装

Jython や IronPython には GIL がない。

Python でマルチスレッドを使う際は、GIL の特性を理解して、I/O バウンドな処理に活用するのが効果的です。