17

を使用してPythonモジュールをインタラクティブにロードする関数がいくつかあります__import__

最近、Python の「インポート ロック」、つまり (GIL だけでなく) インポート専用のロックに関する記事を見つけました。しかし、記事が古いので、それはもう真実ではないかもしれません。

これは、スレッドでのインポートの慣行について疑問に思います。

  1. import/は__import__スレッドセーフですか?
  2. デッドロックを作成できますか?
  3. スレッド化されたアプリケーションでパフォーマンスの問題を引き起こす可能性はありますか?

2012年9月12日編集

Soravux さん、すばらしい回答をありがとうございます。__import__したがって、インポートはスレッド セーフであり、コードで使用する関数が互いに呼び出されないため、デッドロックについて心配する必要はありません。

モジュールが既にインポートされている場合でも、ロックが取得されているかどうか知っていますか? その場合は、sys.modules を調べて、モジュールを呼び出す前にモジュールが既にインポートされているかどうかを確認する必要があります__import__

とにかくGILがあるので、これはCPythonに大きな違いをもたらさないはずです。ただし、Jython やスタックレス python などの他の実装では大きな違いが生じる可能性があります。

2012年9月19日編集

Jythonについては、ドキュメントで次のように述べています。

http://www.jython.org/jythonbook/en/1.0/Concurrency.html#module-import-lock

ただし、Python は、Jython によって実装されるモジュール インポート ロックを定義します。このロックは、任意の名前のインポートが行われるたびに取得されます。これは、インポートが import ステートメント、同等の__import__ビルトイン、または関連するコードを通過するかどうかに関係なく当てはまります。対応するモジュールが既にインポートされている場合でも、モジュールのインポート ロックは、短時間だけでも取得されることに注意することが重要です。

したがって、ロックの取得を避けるために、インポートを行う前に sys.modules をチェックインすることは理にかなっているようです。どう思いますか?

4

2 に答える 2

18

更新: Python 3.3 以降、インポート ロックはグローバルではなくモジュールごとにimpなり、importlib. 変更ログこの問題のチケットに関する詳細情報。

以下の元の回答は Python 3.3 よりも前のものです

通常のインポートは、実行前にインポート ロックを取得し、インポートが完了すると解放されるため、スレッド セーフです。利用可能なフックを使用して独自のカスタム インポートを追加する場合は、必ずこのロック スキームを追加してください。impPython のロック機能は、モジュール ( imp.lock_held()/ acquire_lock()/ )からアクセスできますrelease_lock()編集:これは Python 3.3 以降非推奨であり、ロックを手動で処理する必要はありません。

このインポート ロックを使用しても、既に知られている循環依存関係を除いて、デッドロックや依存関係エラーは発生しません(モジュールaはモジュールbをインポートするモジュールをインポートしますa)。編集: Python 3.3 は、循環インポートによって引き起こされるデッドロックを防ぐために、モジュールごとのロック メカニズム用に変更されました。

新しいプロセスまたはスレッドを作成する方法は複数あります。たとえばfork、およびclone(Linux 環境を想定)。新しいプロセスを作成するとき、それぞれの方法で異なるメモリ動作が生成されます。デフォルトでは、フォークはほとんどのメモリ セグメント (データ (多くの場合 COW)、スタック、コード、ヒープ) をコピーし、そのコンテンツを子と親の間で事実上共有しません。clone(スレッドと呼ばれることが多く、これは Python がスレッド化に使用するものです)の結果は、スタックを除くすべてのメモリ セグメントをその親と共有します。Python のインポート メカニズムは、スタックに配置されないグローバル名前空間を使用するため、スレッド間で共有セグメントを使用します。これは、すべてのメモリ変更 (スタックを除く) によって実行されることを意味します。importスレッド内の他のすべての関連スレッドと親に表示されます。インポートされたモジュールが Python のみの場合、設計上スレッドセーフです。インポートされたモジュールが Python 以外のライブラリを使用している場合は、それらがスレッド セーフであることを確認てください。

ちなみに、Python のスレッド化されたプログラムはGILに悩まされており、プログラムが I/O バウンドであるか、C または外部のスレッドセーフ ライブラリに依存していない限り (実行前に GIL を解放する必要があるため)、パフォーマンスを大幅に向上させることはできません。この GIL のために、インポートされた同じ Python 関数を 2 つのスレッドで実行すると、同時に実行されません。これは CPython の単なる制限であり、Python の他の実装は異なる動作をする可能性があることに注意してください。

編集に答えるには: インポートされたモジュールはすべて Python によってキャッシュされます。モジュールが既にキャッシュにロードされている場合、再度実行されることはなく、インポート ステートメント (または関数) がすぐに返されます。sys.modules でキャッシュ ルックアップを自分で実装する必要はありません。Python がそれをimp行い、sys.modules ルックアップの GIL を除いて、何もロックしません。

2 番目の編集に答えるには: 使用するライブラリ (この場合は標準ライブラリ) への呼び出しを最適化するよりも、単純なコードを維持する必要があることを好みます。通常、何かを実行するのに必要な時間は、それを実行するモジュールをインポートするのに必要な時間よりもはるかに重要です。さらに、プロジェクト全体でこの種のコードを維持するために必要な時間は、実行にかかる時間よりもはるかに長くなります。要するに、「プログラマーの時間は CPU 時間よりも価値がある」ということです。

于 2012-09-12T15:04:24.020 に答える