协程是 Unity3D 中的一个新线程吗? [英] Is coroutine a new thread in Unity3D?

查看:28
本文介绍了协程是 Unity3D 中的一个新线程吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对 协程(在 Unity3D 或其他地方)工作.协程是一个新线程吗?Unity 的 documentation 他们说:

<块引用>

协程是一个可以暂停执行(yield)直到给定的 YieldInstruction 完成的函数.

他们有 C# 示例这里:

使用UnityEngine;使用 System.Collections;公共类示例:MonoBehaviour {无效开始(){打印(开始" + Time.time);StartCoroutine(WaitAndPrint(2.0F));print("WaitAndPrint 完成之前" + 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 语句是一种特殊的返回,确保该函数在下次调用时从 yield 语句之后的行继续."如果yield 是一个特殊的return,那么return 在这里做什么?

  3. 为什么我们必须返回一个IEnumerator?

  4. StartCoroutine 是否会启动一个新线程?

  5. 在上面的例子中,WaitAndPrint() 被调用了多少次?yield return new WaitForSeconds(waitTime); 真的返回了吗?如果是,那么我猜 WaitAndPrint() 在上面的代码中被调用了两次.我猜 StartCoroutine() 多次调用 WaitAndPrint() .但是,我看到 另一个 Unity 文档 说:可以使用 yield 语句在任何时候暂停协程的执行.yield 返回值指定协程何时恢复.这些话让我觉得WaitAndPrint()居然还没有回来;它只是暂停了;它正在等待 WaitForSeconds() 返回.如果是这种情况,那么在上面的代码中WaitAndPrint()只被调用了一次,而StartCoroutine只是负责启动函数,而不是多次调用.

解决方案

协程是一种非常强大的技术,用于模拟 .net4.5 中的 async/await 支持的各种功能,但在早期版本 ( c# >=v2.0).

Microsoft CCR(读一读)也雇用(雇用?) 这种方法.

让我们解决一件事.yield 本身是无效的,后面总是跟着 returnbreak.

考虑一个标准的 IEnumerator(不产生流控制消息).

IEnumerator YieldMeSomeStuff(){产生你好";Console.WriteLine("foo!");产出世界";}

现在:

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

输出是什么?

<前>你好噗!世界

请注意,在我们第二次调用 MoveNext 之前,在 Enumerator 产生world"之前,一些代码在 Enumerator 中运行.这意味着在 Enumerator 中,我们可以编写代码,执行直到遇到 yield return 语句,然后简单地暂停,直到有人调用 MoveNext(轻松处理所有状态/变量被整齐地捕获,所以我们可以从我们离开的地方继续).在MoveNext 调用之后,yield return 语句之后的下一位代码可以运行,直到到达另一个yield return.因此,我们现在可以通过对枚举器的 MoveNext 调用来控制 yield return 语句之间代码的执行.

现在,假设我们的枚举器不是产生字符串,而是产生一条消息,该消息告诉 MoveNext 的调用者,请在调用之前等待 x (waitTime) 秒MoveNext 再次.调用者被写入理解"各种消息.这些消息将始终遵循请在再次调用 MoveNext 之前等待此类事件发生".

现在我们有一种强大的方法可以暂停和重新启动需要满足其他条件才能继续运行的代码,而无需将该功能写入另一种方法,例如在没有协程的情况下执行异步操作.如果没有协程,您将被迫传递一个可怕的异步状态对象,您需要手动组装该对象以捕获一个方法结束和另一个方法开始之间的状态,在一些异步内容之后).协程消除了这一点,因为范围是保留的(通过编译器魔术),因此您的局部变量会在长期存在的异步内容中持续存在.

StartCoroutine 只是启动整个过程.它在 Enumerator 上调用 MoveNext ......一些代码在 Enumerator 中运行...... Enumerator 产生一个控制消息,它通知 StartCoroutine 中的代码何时调用 再次移动下一个.这不需要在新线程中发生,但在多线程场景中会很方便,因为我们可以从不同的线程调用 MoveNext 并控制工作完成的位置.

I am confused and curious about how coroutines (in Unity3D and perhaps other places) work. Is coroutine a new thread? Unity's documentation they said:

A coroutine is a function that can suspend its execution (yield) until the given YieldInstruction finishes.

And they have C# examples here:

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);
    }
}

I have many questions about this example:

  1. In the example above, which line is the coroutine? Is WaitAndPrint() a coroutine? Is WaitForSeconds() a coroutine?

  2. In this line: yield return new WaitForSeconds(waitTime);, why both yield and return are present? I read in Unity documentation that "The yield statement is a special kind of return, that ensures that the function will continue from the line after the yield statement next time it is called." If yield is a special return, what is return doing here?

  3. Why do we have to return an IEnumerator?

  4. Does StartCoroutine start a new thread?

  5. How many times has WaitAndPrint() been called in the above example? Did yield return new WaitForSeconds(waitTime); really returned? If yes then I guess WaitAndPrint() was called twice in the above code. And I guess StartCoroutine() was calling WaitAndPrint() multiple times. However, I saw another Unity documentation that says: "The execution of a coroutine can be paused at any point using the yield statement. The yield return value specifies when the coroutine is resumed." These words make me feel that WaitAndPrint() actually has not returned; it was merely paused; it was waiting for WaitForSeconds() to return. If this is the case, then in the above code WaitAndPrint() was called only once, and StartCoroutine was just responsible for starting the function, not calling it multiple times.

解决方案

Coroutines are an extremely powerful technique used to emulate the kinds of features supported by the async/await in .net4.5, but in earlier versions ( c# >= v2.0 ) .

Microsoft CCR (take a read) also employs (employed?) this approach.

Let's get one thing out of the way. yield alone is not valid and is always followed by either return or break.

Think about a standard IEnumerator (that doesn't yield flow control messages).

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

Now:

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

What's the output?

hello
foo!
world

Notice how, the second time we called MoveNext, before the Enumerator yielded "world" some code ran within the Enumerator. What this means is that in the Enumerator, we can write code that executes until it hits a yield return statement, then simply pauses until someone calls MoveNext (handily with all state/variables neatly captured, so we can pick up where we left off). After a MoveNext call, the next bit of code after the yield return statement can run until another yield return is reached. So we can now control the execution of the code between the yield return statements with the MoveNext call to the Enumerator.

Now, say instead of yielding strings, our Enumerator were to yield a message that says to the caller of MoveNext, "please hang around for x (waitTime) seconds before you call MoveNext again". The caller is written to "understand" a variety of messages. These messages will always be along the lines of "please wait for such and such to happen before calling MoveNext again".

Now we have a powerful means of pausing and restarting code that requires other conditions to be met before it can proceed, without having to write that functionality into another method, like doing async stuff without coroutines. Without coroutines, you're forces to pass around a horrible async state object that you would need to manually assemble to capture state between the end of one method and the starting of another after some async stuff). Coroutines eliminate this because scope is preserved (by compiler magic), so your local variables persist over long lived async stuff.

StartCoroutine simply starts the whole process. It calls MoveNext on the Enumerator... some code runs in the Enumerator... The enumerator yields a control message, which informs the code in StartCoroutine when to call MoveNext again. This need not happen in a new Thread, but can be handy in multithreaded scenarios because we can call MoveNext from different threads and control where the work is done.

这篇关于协程是 Unity3D 中的一个新线程吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆