サーチ…


備考

なぜGILがあるのですか?

GILは1992年にPythonスレッドの開始以来、CPythonを中心に活動してきました。Pythonコードを実行するスレッドの安全性を保証するように設計されています。 GILで書かれたPythonインタプリタは、複数のネイティブスレッドがPythonバイトコードを一度に実行するのを防ぎます。これにより、プラグインはコードがスレッドセーフであることを容易に確認できます。単にGILをロックするだけで、アクティブなスレッドのみを実行できるため、コードは自動的にスレッドセーフです。

短いバージョン:GILは、どれだけのプロセッサとスレッドを持っていても、 一度に実行できるPythonインタプリタのスレッドは1つだけです。

これには使い勝手の点で多くのメリットがありますが、同様に多くの負のメリットもあります。

GILはPython言語の必要条件ではないことに注意してください。したがって、標準のPythonコードから直接GILにアクセスすることはできません。 Pythonのすべての実装がGILを使用するわけではありません。

GILを持つインタプリタ: CPython、PyPy、Cython(ただし、GILをnogil無効にすることができます)

GILを持たないインタプリタ: Jython、IronPython

GILの運営方法に関する詳細:

スレッドが実行されているとき、スレッドはGILをロックします。実行したいスレッドがGILを要求し、スレッドが使用可能になるまで待機します。バージョン3.2より前のCPythonでは、実行中のスレッドは特定の数のPython命令をチェックして、他のコードがロックを要求しているかどうかを確認します(つまり、ロックを解放してから再度要求しました)。この方法では、待機スレッドが起床する前に、ロックを解放したスレッドが再びスレッドを獲得するため、スレッドの不足が発生する傾向がありました。 3.2以降、GILをしたいスレッドはしばらくの間ロックを待機し、その時間が経過すると、実行中のスレッドを強制的に生成する共有変数を設定します。ただし、これにより実行時間が大幅に長くなる可能性があります。詳細については、dabeaz.comの下のリンクを参照してください(参照セクション内)。

スレッドがI / O操作を実行すると、CPythonは自動的にGILを解放します。イメージ処理ライブラリとnumpy number crunchingオペレーションは、処理を行う前にGILを解放します。

GILの利点

GILを使用する通訳者の場合、GILは全身性です。これは、アプリケーションの状態を保持するために使用されます。メリット:
  • GILがロックされている間、ガベージコレクション - スレッドセーフ参照カウントを変更する必要があります。 CPythonでは、garbargeコレクションはすべてGILに結びついています。これは大きなものです。もしGILを削除したいのであれば、まだ機能していなければならないものの詳細については、GILについてのpython.org wikiの記事を参照してください。
  • GILを扱うプログラマーが楽になる - すべてをロックするのは簡単ですが、簡単にコードすることができます
  • モジュールの他の言語からのインポートを容易にします。

GILの結果

GILは、あるスレッドがPythonインタプリタの内部で一度にpythonコードを実行できるようにします。つまり、厳密なPythonコードを実行するプロセスのマルチスレッド化は機能しません。 GILに対してスレッドを使用すると、単一のスレッドで実行した場合よりもスレッドのパフォーマンスが低下する可能性が高くなります。

参考文献:

https://wiki.python.org/moin/GlobalInterpreterLock - それが何をすべきかの簡単な概要、すべてのメリットに関する詳細

http://programmers.stackexchange.com/questions/186889/why-was-python-written-with-the-gil - 明確に書かれた要約

http://www.dabeaz.com/python/UnderstandingGIL.pdf - GILがどのように機能し、なぜ複数のコアで遅くなるのか

http://www.dabeaz.com/GIL/gilvis/index.html - GILがスレッドをロックする方法を示すデータの視覚化

http://jeffknupp.com/blog/2012/03/31/pythons-hardest-problem/ - GIL問題の歴史を簡単に理解する

https://jeffknupp.com/blog/2013/06/30/pythons-hardest-problem-revisited/ - GILの制限を回避する方法の詳細

マルチプロセッシング。プール

簡単な答えは、Pythonでスレッドを使う方法を尋ねるときです:「しないでください。プロセスを使用してください。」マルチプロセッシングモジュールでは、スレッドの作成と同様の構文でプロセスを作成できますが、便利なPoolオブジェクトを使用することをお勧めします。

David Beazleyが最初にGILに対するスレッドの危険性を示すために使用したコードを使用して マルチプロセッシングを使用して書き直します。 プール

デビッドBeazleyのコードは、GILスレッドの問題を示した

from threading import Thread
import time
def countdown(n):
    while n > 0:
        n -= 1

COUNT = 10000000

t1 = Thread(target=countdown,args=(COUNT/2,))
t2 = Thread(target=countdown,args=(COUNT/2,))
start = time.time()
t1.start();t2.start()
t1.join();t2.join()
end = time.time()
print end-start
マルチプロセッシングを使って書き直しました.Pool:
import multiprocessing
import time
def countdown(n):
    while n > 0:
        n -= 1

COUNT = 10000000

start = time.time()
with multiprocessing.Pool as pool:
    pool.map(countdown, [COUNT/2, COUNT/2])

    pool.close()
    pool.join()

end = time.time()
print(end-start)

スレッドを作成する代わりに、新しいプロセスが作成されます。各プロセスはそれ自体のインタプリタであるため、GILの衝突はありません。上記の例では2つしか必要ありませんが、マシン上にコアがあるほど多くのプロセスをオープンします。現実のシナリオでは、マシン上にプロセッサーと同じ長さ以上の長さを持つようにリストを設計したいと考えています。プールは、作成したプロセスの数まで、各引数で実行するように指示する関数を実行します。関数が終了すると、リスト内の残りの関数がそのプロセスで実行されます。

私は、 withステートメントを使用しwithも、プールを閉じて結合しないと、プロセスが存在し続けることがわかりました。リソースをクリーンアップするために、私は常にプールを閉じて参加します。

Cython nogil:

Cythonは別のPythonインタプリタです。 GILを使用しますが、GILを無効にすることができます。 ドキュメントを参照しください

例として、 David Beazleyが最初にGILに対するスレッドの危険性を示すために使用たコードを使用して、nogilを使用して書き直します。

デビッドBeazleyのコードは、GILスレッドの問題を示した

from threading import Thread
import time
def countdown(n):
    while n > 0:
        n -= 1

COUNT = 10000000

t1 = Thread(target=countdown,args=(COUNT/2,))
t2 = Thread(target=countdown,args=(COUNT/2,))
start = time.time()
t1.start();t2.start()
t1.join();t2.join()
end = time.time()
print end-start

ノギルを使って書き直しました(サイロンでの作品のみ):

from threading import Thread
import time
def countdown(n):
    while n > 0:
        n -= 1

COUNT = 10000000

with nogil:
    t1 = Thread(target=countdown,args=(COUNT/2,))
    t2 = Thread(target=countdown,args=(COUNT/2,))
    start = time.time()
    t1.start();t2.start()
    t1.join();t2.join()
    
end = time.time()
print end-start

あなたがcythonを使っている限り、それは簡単です。ドキュメントでは、Pythonオブジェクトを変更しないように注意する必要があることに注意してください。

文の本文にあるコードは、Pythonオブジェクトを決して操作してはいけません.GILを最初に再取得しなければ、Pythonオブジェクトを操作するものを呼び出さないでください。 Cythonは現在これをチェックしていません。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow