なぜグローバル通訳ロックなのか?


89

Pythonのグローバルインタープリターロックの正確な機能は何ですか?バイトコードにコンパイルされる他の言語も同様のメカニズムを採用していますか?


6
また、「それは重要ですか?」
S.Lott、2008年

2
私は同意します。2.6ではマルチプロセッシングモジュールが追加され、スレッドのような方法で複数のプロセスを使用してプログラミングできるようになったので、これは問題ではないと考えています。 docs.python.org/library/multiprocessing.html
monkut

回答:


69

一般に、スレッドセーフの問題では、内部データ構造をロックで保護する必要があります。これは、さまざまなレベルの粒度で実行できます。

  • 個別の構造ごとに独自のロックがある細粒度ロックを使用できます。

  • 1つのロックですべてを保護する粗粒度ロックを使用できます(GILアプローチ)。

各方法にはさまざまな長所と短所があります。きめの細かいロックにより、並列性が向上します。2つのスレッドがリソースを共有しない場合は、並列で実行できます。ただし、はるかに大きな管理オーバーヘッドがあります。コードのすべての行について、いくつかのロックを取得して解放する必要がある場合があります。

大まかなアプローチはその逆です。2つのスレッドを同時に実行することはできませんが、個々のスレッドは簿記をあまり行わないため、より速く実行されます。最終的には、シングルスレッドの速度と並列処理の間のトレードオフになります。

PythonでGILを削除しようとする試みはいくつかありますが、シングルスレッドマシンの余分なオーバーヘッドは一般に大きすぎます。ロックの競合が原因で、マルチプロセッサマシンでも実際には遅くなる場合があります。

バイトコードにコンパイルされる他の言語も同様のメカニズムを採用していますか?

これはさまざまであり、実装プロパティほど言語プロパティと見なすべきではありません。たとえば、JythonやIronPythonなどのPython実装には、GILアプローチではなく、基盤となるVMのスレッドアプローチが使用されます。さらに、Rubyの次のバージョンは、GILの導入に向かっているようです。


1
これを説明できますか?「2つのスレッドを同時に実行することはできません」?最近、Pythonでマルチスレッドを使用した簡単なWebサーバーを作成しました。クライアントからの新しい要求ごとに、サーバーはその新しいスレッドを生成し、それらのスレッドは実行を続けます。それで、同時に複数のスレッドが実行されますよね?または私は間違った方法で理解しましたか?
aviファイル

1
@avi AFAIK pythonスレッドは同時に実行できませんが、1つのスレッドが他のスレッドをブロックする必要があるという意味ではありません。GILは、一度に1つのスレッドのみがPythonコードを解釈できることを意味します。それは、スレッド管理とリソース割り当てが機能しないことを意味するものではありません。
Benproductions1 2014

2
^そのため、どの時点でも、クライアントにコンテンツを提供するスレッドは1つだけです。そのため、実際にマルチスレッドを使用してパフォーマンスを向上させるポイントはありません。正しい?
avi

そしてもちろん、Javaはバイトコードにコンパイルされており、非常に細かいロックが可能です。
Warren Dew

3
@avi、ウェブサーバーのようなIOバウンドプロセスは、Pythonスレッドから引き続き利益を得ることができます。2つ以上のスレッドが同時にIOを実行できます。同時に解釈することはできません(CPU)。
Saish

33

以下は、公式のPython / C APIリファレンスマニュアルからの抜粋です。

Pythonインタープリターは完全にスレッドセーフではありません。マルチスレッドのPythonプログラムをサポートするために、Pythonオブジェクトに安全にアクセスするには、現在のスレッドが保持する必要のあるグローバルロックがあります。ロックがなければ、最も単純な操作でもマルチスレッドプログラムで問題が発生する可能性があります。たとえば、2つのスレッドが同じオブジェクトの参照カウントを同時にインクリメントすると、参照カウントが2回ではなく1回だけ増加することになります。

したがって、グローバルインタープリターロックを取得したスレッドのみがPythonオブジェクトを操作したり、Python / C API関数を呼び出したりできるというルールがあります。マルチスレッドのPythonプログラムをサポートするために、インタプリタは定期的にロックを解放して再取得します-デフォルトでは、100バイトコードの命令ごとに(これはsys.setcheckinterval()で変更できます)。ロックも解放され、ファイルの読み取りや書き込みなどの潜在的にブロックされているI / O操作の周りに再取得されるため、I / Oを要求するスレッドがI / O操作の完了を待機している間に他のスレッドを実行できます。

私はそれが問題をかなりうまくまとめていると思います。


1
私もそれを読みましたが、Pythonがこの点で、たとえばjavaとなぜ違うのか理解できません(そうですか?)
Federico A. Ramponi 2008年

@EliBendersky Pythonのスレッドはpthreadのように実装され、OS(によって処理されるdabeaz.com/python/UnderstandingGIL.pdf Javaスレッドがアプリケーションレベルのスレッド玉葉スケジューリングがJVMによって処理されているのに対し)
gokul_uf

19

グローバルインタープリターロックは、参照カウンターがホースから保護される、大きなミューテックスタイプのロックです。純粋なPythonコードを記述している場合、これはすべて裏で行われますが、PythonをCに埋め込む場合は、明示的にロックを取得または解放する必要があります。

このメカニズムは、バイトコードにコンパイルされるPythonとは関係ありません。Javaには必要ありません。実際、それはJython(pythonがjvmにコンパイルされている)でも必要ありません。

この質問も参照してください


4
「このメカニズムは、バイトコードにコンパイルされるPythonとは関係ありません」:正確には、これはCPython実装のアーティファクトです。他の実装(前述のJythonなど)では、スレッドセーフな実装のおかげでこの制限がありません
Eli Bendersky

11

Pythonは、perl 5と同様に、スレッドセーフになるようにゼロから設計されていません。スレッドは事後に移植されたので、グローバルインタープリターロックを使用して、インタープリターの腸内の特定の時間に1つのスレッドのみがコードを実行している場所への相互排除を維持します。

個々のPythonスレッドは、ロックを頻繁に循環させることにより、インタープリター自体によって協調的にマルチタスク化されます。

他のPythonスレッドがこのプロトコルに「オプトイン」するためにアクティブであるときにCからPythonと通信しているときに、自分でロックを取得する必要があります。

後でマルチスレッドシステムに進化したシングルスレッドの遺産を持つ他のシステムは、多くの場合、この種のメカニズムを備えています。たとえば、Linuxカーネルには、初期のSMP時代からの「ビッグカーネルロック」があります。時間の経過とともに、マルチスレッドのパフォーマンスが問題になるにつれて、これらの種類のロックをより小さな部分に分割するか、可能な場合は、それらをロックフリーのアルゴリズムとデータ構造に置き換えて、スループットを最大化する傾向があります。


+1は、ほとんどの考えよりも粗いロックが使用されていること、特に忘れられがちなBKLを使用していることを言及している(私が使用しているreiserfs-私がそれについて知っている唯一の本当の理由)。
new123456

3
LinuxにはBKLがあり、バージョン2.6.39以降、BKLは完全に削除されました。
aviファイル

5
もちろん。私が質問に答えてから約3年後のことを覚えておいてください。=)
Edward KMETT 2013

7

2番目の質問に関しては、すべてのスクリプト言語がこれを使用しているわけではありませんが、それはそれらの能力を弱めるだけです。たとえば、Rubyのスレッドは緑色でネイティブではありません。

Pythonでは、スレッドはネイティブであり、GILはそれらが異なるコアで実行されるのを防ぎます。

Perlでは、スレッドはさらに悪化します。それらはインタプリタ全体をコピーするだけで、Pythonのように使用可能になるにはほど遠いです。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.