グローバルインタープリターロック(GIL)は、スレッド処理などがPythonでややこしい理由の主な理由としてよく引用されているようです。
プログラマーではないので、なぜそうなるのか手がかりがありません-GILを入れる背後にあるロジックは何ですか?
グローバルインタープリターロック(GIL)は、スレッド処理などがPythonでややこしい理由の主な理由としてよく引用されているようです。
プログラマーではないので、なぜそうなるのか手がかりがありません-GILを入れる背後にあるロジックは何ですか?
回答:
Pythonには、CPython、IronPython、RPythonなど、いくつかの実装があります。
GILを持っている人もいれば持っていない人もいます。たとえば、CPythonにはGILがあります。
http://en.wikipedia.org/wiki/Global_Interpreter_Lockから
GILを使用してプログラミング言語で記述されたアプリケーションは、各プロセスに独自のインタープリターがあり、独自のGILがあるため、個別のプロセスを使用して完全な並列処理を実現するように設計できます。
GILの利点
Python(CPythonなど)がGILを使用する理由
CPythonでは、グローバルインタープリターロック(GIL)は、複数のネイティブスレッドがPythonバイトコードを同時に実行することを防ぐミューテックスです。このロックは、主にCPythonのメモリ管理がスレッドセーフではないために必要です。
GILは、特定の状況でマルチスレッドCPythonプログラムがマルチプロセッサシステムを最大限に活用できないため、物議を醸しています。I / O、画像処理、NumPyの数値計算など、潜在的にブロックまたは長時間実行される操作は、GILの外部で発生することに注意してください。したがって、GILがボトルネックになるのは、CPythonバイトコードを解釈してGIL内で多くの時間を費やすマルチスレッドプログラムのみです。
Pythonには、いくつかの理由により、きめの細かいロックではなくGILがあります。
シングルスレッドの場合は高速です。
I / Oバウンドプログラムのマルチスレッドの場合は高速です。
Cライブラリで計算集中型の作業を行うCPUにバインドされたプログラムのマルチスレッドの場合は高速です。
これにより、C拡張機能の記述が簡単になります。Pythonスレッドの切り替えは、許可する場所(Py_BEGIN_ALLOW_THREADSとPy_END_ALLOW_THREADSマクロの間)以外では行われません。
Cライブラリのラッピングが簡単になります。スレッドの安全性について心配する必要はありません。ライブラリがスレッドセーフでない場合は、呼び出し中にGILをロックしたままにします。
GILはC拡張によってリリースできます。Pythonの標準ライブラリは、ブロッキングI / O呼び出しごとにGILをリリースします。したがって、GILは、I / Oバウンドサーバーのパフォーマンスに影響を与えません。したがって、プロセス(フォーク)、スレッド、または非同期I / Oを使用して、Pythonでネットワークサーバーを作成できます。GILは邪魔になりません。
CまたはFortranの数値ライブラリは、GILがリリースされると同様に呼び出すことができます。C拡張がFFTの完了を待っている間、インタープリターは他のPythonスレッドを実行します。したがって、この場合もGILは、きめの細かいロックよりも簡単で高速です。これは、数値作業の大部分を構成します。NumPy拡張機能は、可能な限りGILをリリースします。
スレッドは通常、ほとんどのサーバープログラムを作成するのに悪い方法です。負荷が低い場合、分岐は簡単です。負荷が高い場合、非同期I / Oおよびイベント駆動型プログラミング(PythonのTwistedフレームワークを使用するなど)の方が優れています。スレッドを使用する唯一の言い訳は、Windowsにos.forkがないことです。
GILは、純粋なPythonでCPUを集中的に使用する場合にのみ問題になります。ここでは、プロセスとメッセージ受け渡し(mpi4pyなど)を使用して、よりクリーンなデザインを取得できます。Pythonチーズショップには「処理」モジュールもあり、プロセスにスレッドと同じインターフェイスを提供します(つまり、threading.Threadをprocessing.Processに置き換えます)。
スレッドを使用して、GILに関係なくGUIの応答性を維持できます。GILがパフォーマンスを低下させる場合(上記の説明を参照)、スレッドにプロセスを生成させ、それが完了するのを待つことができます。
s/RPython/PyPy/g
。@MichaelBorgwardtプロのGILに理由を与えることは、一種の問題のポイントですね。ただし、この回答の内容(つまり、代替案の議論)の一部は重要ではないことに同意します。そして、好むと好まざるとにかかわらず、リカウントは取り除くことはほぼ不可能です。これは、APIとコードベース全体に深く浸透しています。コードの半分を書き直し、すべての外部コードを壊さずにそれを取り除くことはほとんど不可能です。
multiprocessing
ライブラリを忘れないでください-2.6 以降の標準。ワーカープールは、いくつかの単純なタイプの並列処理のための非常に洗練された抽象化です。
まず、PythonにはGILがありません。Pythonはプログラミング言語です。プログラミング言語は、抽象的な数学的規則と制限のセットです。Python Language Specificationには、GILが存在する必要があるという記述はありません。
Pythonにはさまざまな実装があります。GILを持っている人もいれば、持っていない人もいます。
GILを使用する簡単な説明の1つは、並行コードの記述は難しいということです。コードの周りに巨大なロックを配置することで、常に連続して実行するように強制します。問題が解決しました!
特にCPythonでの重要な目標の1つは、Cで記述されたプラグインでインタープリターを簡単に拡張できるようにすることです。繰り返しますが、並行コードの記述は難しいため、並行性がないことを保証することで、拡張機能の記述が容易になります通訳者。さらに、これらの拡張機能の多くは、並行性を念頭に置いて作成されていない可能性がある既存のライブラリの単なる薄いラッパーです。
GILの目的は何ですか?
CAPIドキュメントには、この件に関して次のように記載されています。
Pythonインタープリターは完全にスレッドセーフではありません。マルチスレッドPythonプログラムをサポートするために、グローバルインタープリターロックまたはGILと呼ばれるグローバルロックがあります。これは、Pythonオブジェクトに安全にアクセスする前に現在のスレッドが保持する必要があります。ロックがなければ、最も単純な操作でもマルチスレッドプログラムで問題が発生する可能性があります。たとえば、2つのスレッドが同じオブジェクトの参照カウントを同時にインクリメントすると、参照カウントは2回ではなく1回だけインクリメントされる可能性があります。
つまり、GILは状態の破損を防ぎます。Pythonプログラムは、メモリセーフな操作のみが許可されているため、セグメンテーションフォールトを生成しないでください。GILは、この保証をマルチスレッドプログラムに拡張します。
代替手段は何ですか?
GILの目的が破損から状態を保護することである場合、1つの明白な代替策は、よりきめ細かいロックです。おそらくオブジェクトごとのレベルで。これに伴う問題は、マルチスレッドプログラムのパフォーマンスを向上させることが実証されていますが、オーバーヘッドが増加し、結果としてシングルスレッドプログラムが苦しむことです。