コルーチンは、他のスクリプトコードからは異なる形で実行されます。ほとんどのスクリプトコードは、パフォーマンス出力内で、特定の Unity コールバック呼び出しの下の一箇所にのみ表示されます。ただし、コルーチンの CPU コードは出力内で常に二箇所に表示されます。
あるコルーチンの初期コード(コルーチンメソッドの開始から初回イールドまで)の全ては、そのコルーチンの開始された場所に表示されます。通常はこれは、 StartCoroutine
メソッドが呼び出された場所になります。 Unity コールバックから生成されるコルーチン([例] IEnumerator
を戻す Start
コールバック)は、最初にそれぞれの Unity コールバック内に表示されます。
コルーチンのその他のコード(初回再開時点から実行完了まで)は全て、 Unity のメインループ内に表示される DelayedCallManager
ライン内に表示されます。
なぜ上記のようになるか理解するために、コルーチンの実行される仕組みを考えてみましょう。
コルーチンは、 C# コンパイラーによって自動生成されるクラスのインスタンスに基づいています。このオブジェクトは、(プログラマーにとっては単一のメソッドである)複数の呼び出しにわたってコルーチンのステート(状態)をトラッキングするために必要とされます。コルーチン内のローカルスコープ変数は複数の yield
コールにわたって維持されなければならないため、これらのローカルスコープ変数は生成されたクラス内に巻き上げされ、その結果、コルーチンの持続時間を通してヒープに割り当てられたままになります。また、このオブジェクトはコルーチンの内部ステートのトラッキングも行っており、コード内のどの時点で、イールド後にコルーチンが再開しなければならないかを記憶しています。
このため、コルーチンの開始によって掛かるメモリ負荷は、一定のオーバーヘッドコストに、そのローカルスコープ変数のサイズを足したものということになります。
コルーチンを開始させるコードがこのオブジェクトの構築と呼び出しを行い、その後このコルーチンのイールド条件が満たされた時点で、 Unity の DelayedCallManager
がそれを再度呼び出します。コルーチンは通常別のコルーチンの外で開始されるので、その実行コストは上述の 2 箇所に分割されます。
上のスクリーンショット内では、 DelayedCallManager
がいくつかの異なるコルーチンを再開している箇所がこれに当たります。主なものは PopulateCharacters
、 AsyncLoad
、 LoadDatabase
です。
一連の処理がある場合、可能であれば、個々のコルーチンの数がなるべく少なくなるように凝縮することをお勧めします。入れ子になったコルーチンは、コードの明瞭化や維持の面では大変優れていますが、オブジェクトがコルーチンをトラッキングしているため、メモリオーバーヘッドが高くなってしまいます。
あるコルーチンがほぼ毎フレーム実行されていて、長期間の処理long-running operationsをイールドしていない場合は、一般的には、それを Update
または LateUpdate
コールバックに置き換えたほうがより読み易くなります。特にlong-running or infinitely-loopingコルーチンにおいてはこれが顕著ですparticularly true。
コルーチンは、オブジェクトが無効になっても停止しません。確実に破棄されたときにのみ停止します。これにより、例えば、コルーチンが実行され、必要に応じてオブジェクトを再度有効にすることができます。Destroy(this) の呼び出しは即座に OnDisable
を起動し、コルーチンが処理されます。 最後に、フレームの最後に OnDestroy
が呼び出されます。
コルーチンはスレッドでは ない ことを覚えておくことが重要です。コルーチン内で実行される同期操作は、メインスレッドでも実行されます。 目標がメインスレッドに費やされる CPU 時間を削減することであるならば、他のスクリプトコード内と同じようにコルーチン内の操作をブロックしないようにすることも同様に重要です。
コルーチンは、長時間の非同期処理を扱う場合に使用するのが最適です。例えば HTTP 通信やアセットの読み込み、ファイル出入力の完了の待機などです。