Pythonマルチスレッド(一):GIL
2000 ワード
最近Pythonのマルチスレッドプログラミングを勉強して、いくつかの文章を書いて記録します.
GILはGlobal Interpreter Lock、すなわちグローバル解釈ロックの略であり、同じ時刻に1つのスレッドだけが1つのCPU上でバイトコードを実行し、複数のスレッドを複数のCPUにマッピングできないことを保証している.これはCPython解釈器の欠陥であり、CPythonはほとんどの環境でデフォルトのPython実行環境であり、多くのライブラリがCPythonに基づいて作成されているため、GILをPythonの問題にまとめる人が多い.
GILはスレッドのセキュリティを保護するように設計されており,マルチスレッド共有変数のため,スレッド同期がうまくいかなければ,マルチスレッドはスレッドを混乱させやすい.実際にGILがあっても、この問題は完全に解決できません.GILは実際に解放され、あるスレッドの実行が完了した後に解放されるのではなく、コードのバイトコードやタイムスライスに基づいて解放されるので、次の例です.
このプログラムは直感的に見ると、
結果は次のとおりです.
もう1つの条件は、GIL解放をもたらす.それは、プログラムがIOオペレーションに遭遇し、
GILはGlobal Interpreter Lock、すなわちグローバル解釈ロックの略であり、同じ時刻に1つのスレッドだけが1つのCPU上でバイトコードを実行し、複数のスレッドを複数のCPUにマッピングできないことを保証している.これはCPython解釈器の欠陥であり、CPythonはほとんどの環境でデフォルトのPython実行環境であり、多くのライブラリがCPythonに基づいて作成されているため、GILをPythonの問題にまとめる人が多い.
GILはスレッドのセキュリティを保護するように設計されており,マルチスレッド共有変数のため,スレッド同期がうまくいかなければ,マルチスレッドはスレッドを混乱させやすい.実際にGILがあっても、この問題は完全に解決できません.GILは実際に解放され、あるスレッドの実行が完了した後に解放されるのではなく、コードのバイトコードやタイムスライスに基づいて解放されるので、次の例です.
import threading
total = 0
def add():
global total
for i in range(1000000):
total += 1
def desc():
global total
for i in range(1000000):
total -= 1
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(total)
このプログラムは直感的に見ると、
total
に100000を加えて100000を減らし、どのスレッドが先に実行されても、最後の結果は0であるはずですが、上記のコードを何度も許可すると、コードの結果が毎回異なり、プラスとマイナスがあることがわかります.その理由はGILの解放にかかわる.まず、一般的な加算関数のバイトコードを確認します.import dis
def add1(a):
a += 1
return a
print(dis.dis(add1))
結果は次のとおりです.
2 0 LOAD_FAST 0 (a)
2 LOAD_CONST 1 (1)
4 INPLACE_ADD
6 STORE_FAST 0 (a)
3 8 LOAD_FAST 0 (a)
10 RETURN_VALUE
None
a += 1
の実行過程は、変数a
をCPUにロードし、定数1
をCPUにロードした後、加算操作を実行し、最後にa
をメモリに格納することである.GILはPythonコードセグメントに従って解放されるのではなく、バイトコードやタイムスライスに従って解放されるため、前の例では、add
関数が加算後もメモリに保存されていなければ、GIL解放、desc
関数が実行権を獲得し、このときロード時にロードする変数total
が加算操作されていないtotal
である、従って,従来のadd
に相当する関数は機能せず,複数回のループを行った後,プログラムの実行結果は自然に0ではない.このことを競合条件(race condition)と呼び,GILがなくてもこのような問題が発生する.解決策はロックメカニズムを用いることであり,後述する.もう1つの条件は、GIL解放をもたらす.それは、プログラムがIOオペレーションに遭遇し、
time.sleep
がプログラムをブロックすると、マルチスレッドがIOオペレーションの問題を処理するのに非常に有効である.