ここで行うべき正しいことは、シャットダウンに頼るのではなく、ある時点で明示的に強い参照を解放することです。
特に、モジュールがリリースされた場合、そのグローバルはリリースされます... しかし、モジュールがリリースされることはどこにも文書化されていないようです。そのため、シャットダウン時にオブジェクトへの参照が残っている可能性があります。そして、Martijn Pieters が指摘したように:
__del__()
インタープリターが終了したときにまだ存在しているオブジェクトに対してメソッドが呼び出されることは保証されていません。
ただし、インタープリターが終了する少し前に、オブジェクトへの (弱い) 参照がないことを確認できれば、クリーンアップの実行を保証できます。
ハンドラーを使用atexit
して自分で明示的にクリーンアップできますが、メインモジュールの最後に落ちる前 (または を呼び出すsys.exit
か、最後の非デーモンスレッドを終了するなど) に明示的に行うことができます。最も簡単な方法は、多くの場合、関数全体を取得して、または/main
でラップすることです。with
try
finally
または、もっと簡単に言うと、クリーンアップ コードを__del__
メソッドや weakref コールバックに入れようとしないでください。with
クリーンアップ コード自体をまたはfinally
またはに入れるだけatexit
です。
別の回答へのコメント:
私が実際にやろうとしているのは、通常はタイマーによって開かれているサブプロセスを閉じることですが、プログラムが終了するときに核にする必要があります。これを実行してデーモンサブプロセスを開始し、他のプロセスを個別に監視および強制終了する唯一の本当に「信頼できる」方法はありますか?
この種のことを行う通常の方法は、タイマーを外部から信号を送ることができるものに置き換えることです。アプリのアーキテクチャと使用しているタイマーの種類を知らなくても (たとえば、reactor がタイマーを開始するシングル スレッドの非同期サーバーと、OS タイマー メッセージがタイマーを開始するシングル スレッドの非同期 GUI アプリと、マルチ- タイマーがsleep
間隔の間の単なるスレッドであるスレッド化されたアプリ vs. …)、より具体的に説明するのは困難です。
その間、サブプロセスを処理するためのより簡単な方法があるかどうかを確認することもできます。たとえば、明示的なプロセス グループを使用して、プロセスの代わりにプロセス グループを強制終了するとします (Windows と Unix の両方で、すべての子プロセスが強制終了されますが、詳細は大きく異なります)。あるいは、サブプロセスにパイプを渡して、パイプのもう一方の端がダウンしたときにサブプロセスを終了させますか?
ドキュメントは、残りの参照が削除されたとしても、それらが削除される順序についても保証していないことに注意してください。実際、CPython を使用している場合、Py_Finalize
具体的には「ランダムな順序で行われる」と述べています。
ソースが面白い。明らかに明示的にランダム化されているわけではなく、完全に恣意的というわけでもありません。最初に何もなくなるまで GC 収集を実行し、次に GC 自体をファイナライズし、PyImport_Cleanup
(基本的にはただのsys.modules.clear()
) を実行し、次に別の収集がコメント アウトされ (理由についていくつかの議論があります)、最後に_PyImport_Fini
(定義されている) を実行します。 「内部使用のみ」としてのみ)。
ただし、これは、モジュールが実際にオブジェクトへの唯一の (弱でない) 参照を保持していて、モジュール自体を含む壊れないサイクルがないと仮定すると、モジュールはシャットダウン時にクリーンアップされ、オブジェクトへの最後の参照により、オブジェクトもクリーンアップされます。(もちろん、ビルトイン、拡張モジュール、およびこの時点でまだ存在するものを直接参照しているもの以外は当てにできません…しかし、上記のコードは問題ないはずfoo
ですFoo
。他の非ビルトインに依存しないでください。)
これは CPython 固有であり、実際には CPython 3.3 固有であることに注意してください。確実にするために、バージョンに関連する同等のソースを読みたいと思うでしょう。繰り返しになりますが、ドキュメントには「ランダムな順序で」削除されることが明示されているため、実装固有の動作に依存したくない場合は、それを期待する必要があります。
もちろん、クリーンアップ コードが呼び出される保証はまだありません。たとえば、未処理のシグナル (Unix の場合) または構造化された例外 (Windows の場合) は、何もクリーンアップする機会を与えずにインタープリターを強制終了します。そのためのハンドラーを作成したとしても、誰かが常に電源コードを引っ張ることができます。したがって、完全に堅牢な設計が必要な場合は、いつでもクリーンアップせずに中断できる必要があります (ジャーナリング、アトミック ファイル操作の使用、明示的な承認を伴うプロトコルなどによって)。