25

コルーチン(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存在するのですか? Unity のドキュメントで、「yield ステートメントは特別な種類の return であり、次に呼び出されたときに関数が yield ステートメントの後の行から継続することを保証する」と読みました。yieldが特別な場合、ここreturnで何をreturnしていますか?

  3. を返す必要があるのはなぜIEnumeratorですか?

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

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

4

1 に答える 1

26

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

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

邪魔にならないようにしましょう。alone は無効であり、常にoryieldが続きます。returnbreak

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

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

今:

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

出力は何ですか?

こんにちは
ふー!
世界

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

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

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

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

于 2013-06-06T01:15:49.857 に答える