GIL
Global Interpreter Lock
なくせたらしい
PythonがそのうちGILをなくそうと頑張ってるらしい
@jingbay: Pythonが遂にGlobal Interpreter Lockを前提としないPEP-703 (Making the Global Interpreter Lock Optional in CPython),を3.13にて受け入れる方針 5年後にはno-GIL buildを標準化目標とするが、これをPython4ではないとしてまず互換性重視を継続し問題を炙り出す
インタプリタのスレッドによって保持されるスレッドセーフでないコードを、 他のスレッドと共有してしまうことを防ぐための排他ロックのこと インタプリタの1プロセスに1GILがある
要するに
例えマルチコアであっても。
マルチスレッドになっていたとしても、実際には並行処理を擬似的に実現するために、インタプリタが交互にロックを解放・獲得しているに過ぎない。
GILの影響
PythonはGILがあるので、マルチスレッドにしても同時に実行されるスレッドは1つに制限されている. しかし、I/O待ちの間や、特定のサードパーティの拡張機能により、Cのコードを並列実行できたりする。
なので、CPythonはマルチスレッドをサポートしているにも関わらず、GILがあるので同時に1つのスレッドしか進行できないようになっているので、結局並列計算できていない。むしろ、スレッド立ち上げなどにコストがかかる。
じゃあなんのためにPythonにマルチスレッドがあるのか
マルチスレッドによって、プログラムが同時に複数のことをしているとわかりやすくなる
本来なら、並列計算を実装するのは難しいが、勝手に良い感じに公平に並行処理してくれる
Pythonがある種のシステムコールを行う際に生じるブロッキングI/Oを扱うため
ブロッキングI/Oのときにそれ用のスレッドを立ち上げることで、I/Oにかかる時間に他の処理をすることができる。
Pythonはsystem callを行うときは、直前にGILを解放し、終わるとGILを獲得するようになっているので、システムコールの際はGILの制約を受けることなく並列処理を実行することができる PythonにはGILがあるので、Pythonのバイトコードを一度に1スレッドしか行えない. にほんごがわからん
Pythonインタプリタは内部でticksというカウンタを持っている
スクリプト実行時に一定期間のticks(コードの塊)でスレッドの実行がスイッチされる
ブロッキングI/Oを多く発行するようなスクリプトの場合、I/O要求を出す前に、スレッドがGILを解放
I/Oから戻ったときにGILを取得する
PythonのTheading
複数の関数を同時に動かせる
一つのCPUで実行
スレッドは1つの実行に約8MBのメモリを消費する。
開始にコストがかる(『Effective Python 第2版』.icon p136)
スレッドは切り替えにかかるコストは小さいが、実行されるプログラムがスレッドセーフでないといけない
いつスレッドを使うべきか
応答性の良いインターフェースをつくる
パフォーマンスの向上を目的としてマルチスレッドを使用するわけではなく、裏でする処理が時間がかかる場合でもユーザーがインターフェースを操作できるようにできる
パフォーマンス向上というよりはUX向上?
仕事を委譲する
時間のかかる処理の実行に外部リソースのプロセスに委譲する場合はマルチスレッドで渡す仕事を分類と、結果の受取をする。
外部リソースとはほかのサーバーとかDBとか
マルチユーザーのアプリケーションをつくる
リクエストが送られてくるアプリケーションでユーザーごとにスレッドを立てる
これをマルチプロセスにすると、各プロセスで新しくインタープリタを読み込む必要があるので、リソースがかかる(スレッドの初期化にも時間が掛かるがプロセスほどではない(?))
参考
『エキスパートPythonプログラミング』.icon p.435
2.5 マルチスレッドの実装には気をつけるべき点が多い。
スレッドプールやワーカースレッドとの通信用のキューを用意し、スレッドからの例外を正常に処理し、レート制限機能を提供するのにも実装がスレッドセーフであるか気をつけなければいけない.また、外部ライブラリのパッケージの作者がこのライブラリはスレッドセーフだと保証してくれなければ、本番環境では安心して使えない。 『エキスパートPythonプログラミング』 p.450 GILを実装した言語の例
「グローバルVMロック」と呼ぶ
参考