web-dev-qa-db-ja.com

コルーチンはUnity3Dの新しいスレッドですか?

コルーチン (Unity3Dやおそらく他の場所で)がどのように機能するかについて、私は混乱していて興味があります。コルーチンは新しいスレッドですか? Unityの ドキュメント 彼らは言った:

コルーチンは、指定されたYieldInstructionが終了するまでその実行(yield)を一時停止できる関数です。

そして、C#の例があります ここ

_using UnityEngine;
using System.Collections;

public class example : MonoBehaviour {
    void Start() {
        print("Starting " + Time.time);
        StartCoroutine(WaitAndPrint(2.0F));
        print("Before WaitAndPrint Finishes " + Time.time);
    }
    IEnumerator WaitAndPrint(float waitTime) {
        yield return new WaitForSeconds(waitTime);
        print("WaitAndPrint " + Time.time);
    }
}
_

この例について多くの質問があります。

  1. 上記の例では、コルーチンはどの行ですか? WaitAndPrint()はコルーチンですか? WaitForSeconds()はコルーチンですか?

  2. この行:yield return new WaitForSeconds(waitTime);、なぜyieldreturnの両方が存在するのですか? nityのドキュメント を読みました。「yieldステートメントは特別な種類の戻り値であり、次に呼び出されたときに関数がyieldステートメントの後の行から継続することを保証します。」 yieldが特別なreturnである場合、ここでreturnは何をしていますか?

  3. なぜIEnumeratorを返さなければならないのですか?

  4. StartCoroutineは新しいスレッドを開始しますか?

  5. 上記の例でWaitAndPrint()は何回呼び出されましたか? yield return new WaitForSeconds(waitTime);は本当に返されましたか?はいの場合、上記のコードでWaitAndPrint()が2回呼び出されたと思います。そして、StartCoroutine()WaitAndPrint()を複数回呼び出していたと思います。ただし、 別のUnityドキュメント は、「コルーチンの実行は、yieldステートメントを使用していつでも一時停止できます。yieldの戻り値は、コルーチンがいつ再開されるかを指定します。」と表示されます。これらの言葉は、WaitAndPrint()が実際には戻っていないように感じさせます。一時停止しただけです。 WaitForSeconds()が戻るのを待っていました。この場合、上記のコードでは、WaitAndPrint()は1回だけ呼び出され、StartCoroutineは関数の開始のみを担当し、複数回は呼び出されませんでした。

22
Zening Qu

コルーチンは、.net4.5のasync/awaitでサポートされている機能の種類をエミュレートするために使用される非常に強力な手法ですが、以前のバージョン(c#> = v2.0)では使用されていました。

Microsoft CCR (読んでください)もこのアプローチを採用しています(採用されていますか?)。

邪魔にならないようにしましょう。 yieldだけでは無効であり、その後に常にreturnまたはbreakが続きます。

標準のIEnumerator(フロー制御メッセージを生成しない)について考えてみてください。

IEnumerator YieldMeSomeStuff()
{
    yield "hello";
    Console.WriteLine("foo!");
    yield "world";
}

今:

IEnumerator e = YieldMeSomeStuff();
while(e.MoveNext())
{
    Console.WriteLine(e.Current);
}

出力は何ですか?

こんにちは
 foo!
 world 

列挙子が「ワールド」を生成する前に、2回目にMoveNextを呼び出したときに、列挙子内で実行されたコードに注目してください。つまり、列挙子では、yield returnステートメントに到達するまで実行し、誰かがMoveNextを呼び出すまで一時停止するコードを記述できます(すべての状態/変数が適切にキャプチャされているため、中断したところから再開できます)。 MoveNext呼び出しの後、yield returnステートメントの後のコードの次のビットは、別のyield returnに到達するまで実行できます。これで、列挙子へのMoveNext呼び出しを使用して、yield returnステートメント間のコードの実行を制御できます。

さて、文字列を生成する代わりに、列挙子がMoveNextの呼び出し元にメッセージを生成するとします。 "呼び出す前にx(waitTime)秒間しばらくお待ちください。 MoveNext再び "。発信者は、さまざまなメッセージを「理解」するように書かれています。これらのメッセージは常に "MoveNextを再度呼び出す前にそのようなことが起こるのを待ってください"の行に沿っています。

これで、コルーチンなしで非同期処理を実行するなど、別のメソッドにその機能を書き込むことなく、続行する前に他の条件が満たされる必要があるコードを一時停止および再起動する強力な手段ができました。コルーチンがないと、あるメソッドの終了から別のメソッドの開始までの間に状態をキャプチャするために手動でアセンブルする必要がある恐ろしい非同期状態オブジェクトを強制的に渡す必要があります)。コルーチンはスコープが(コンパイラの魔法によって)保持されるため、これを排除します。そのため、ローカル変数は長期間有効な非同期のものにわたって存続します。

StartCoroutineは単にプロセス全体を開始します。列挙子でMoveNextを呼び出します...一部のコードは列挙子で実行されます...列挙子は制御メッセージを生成し、StartCoroutineのコードにいつMoveNextを呼び出すかを通知します再び。これは新しいスレッドで発生する必要はありませんが、さまざまなスレッドからMoveNextを呼び出して作業の実行場所を制御できるため、マルチスレッドのシナリオで便利です。

21
spender