简体   繁体   English

协程是Unity3D中的新线程吗?

[英]Is coroutine a new thread in Unity3D?

I am confused and curious about how coroutines (in Unity3D and perhaps other places) work. 关于协同程序 (在Unity3D和其他地方)如何工作,我感到困惑和好奇。 Is coroutine a new thread? coroutine是新线程吗? Unity's documentation they said: 他们说Unity的文档

A coroutine is a function that can suspend its execution (yield) until the given YieldInstruction finishes. 协程是一个可以暂停执行(yield)直到给定的YieldInstruction完成的函数。

And they have C# examples here : 他们在这里有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);
    }
}

I have many questions about this example: 我对这个例子有很多疑问:

  1. In the example above, which line is the coroutine? 在上面的例子中,哪一行是协程? Is WaitAndPrint() a coroutine? 是的WaitAndPrint()是一个协程吗? Is WaitForSeconds() a coroutine? WaitForSeconds()是一个协程吗?

  2. In this line: yield return new WaitForSeconds(waitTime); 在这一行中: yield return new WaitForSeconds(waitTime); , why both yield and return are present? ,为什么yieldreturn都存在? 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." 我在Unity文档中读到“yield语句是一种特殊的返回,它确保函数将在下次调用yield语句后继续执行。” If yield is a special return , what is return doing here? 如果yield是一个特殊的return ,那么return在这里做什么?

  3. Why do we have to return an IEnumerator ? 为什么我们必须返回IEnumerator

  4. Does StartCoroutine start a new thread? StartCoroutine是否启动了一个新线程?

  5. How many times has WaitAndPrint() been called in the above example? 在上面的例子中调用了WaitAndPrint()多少次? Did yield return new WaitForSeconds(waitTime); yield return new WaitForSeconds(waitTime); really returned? 真的回来了? If yes then I guess WaitAndPrint() was called twice in the above code. 如果是,那么我猜WaitAndPrint()在上面的代码中被调用了两次。 And I guess StartCoroutine() was calling WaitAndPrint() multiple times. 我猜StartCoroutine()多次调用WaitAndPrint() 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." 但是,我看到另一个Unity文档说:“可以使用yield语句在任何时候暂停执行协程。yield return value指定何时恢复协程。” These words make me feel that WaitAndPrint() actually has not returned; 这些话让我觉得WaitAndPrint()实际上还没有返回; it was merely paused; 它只是暂停了; it was waiting for WaitForSeconds() to return. 它正在等待WaitForSeconds()返回。 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. 如果是这种情况,那么在上面的代码中, WaitAndPrint()只被调用一次,而StartCoroutine只负责启动该函数,而不是多次调用它。

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

Microsoft CCR (take a read) also employs (employed?) this approach. Microsoft CCR (读取)也采用(使用?)这种方法。

Let's get one thing out of the way. 让我们放开一件事。 yield alone is not valid and is always followed by either return or break . 单独的yield无效,并且始终是returnbreak

Think about a standard IEnumerator (that doesn't yield flow control messages). 想想标准的IEnumerator(它不会产生流控制消息)。

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. 请注意,在枚举器产生“世界”之前,我们第二次调用MoveNext ,在枚举器中运行了一些代码。 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). 这意味着在Enumerator中,我们可以编写执行代码直到它yield return语句,然后暂停直到有人调用MoveNext (轻松地将所有状态/变量整齐地捕获,所以我们可以从我们中断的地方继续) 。 After a MoveNext call, the next bit of code after the yield return statement can run until another yield return is reached. MoveNext调用之后, yield return语句之后的下一位代码可以运行,直到达到另一个yield return So we can now control the execution of the code between the yield return statements with the MoveNext call to the Enumerator. 因此,我们现在可以通过对Enumerator的MoveNext调用来控制yield return语句之间的代码执行。

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" . 现在,我们的枚举器不是产生字符串,而是产生一条消息,告诉MoveNext的调用者, “请再次调用MoveNext之前暂停x(waitTime)秒” 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" . 这些消息将始终沿着“请等待再次调用MoveNext之前发生这样的事情”

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. StartCoroutine只是启动整个过程。 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. 它在Enumerator上调用MoveNext ...一些代码在Enumerator中运行...枚举器产生一条控制消息,它通知StartCoroutine的代码何时再次调用MoveNext 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. 这不需要在新的Thread中发生,但在多线程场景中可以很方便,因为我们可以从不同的线程调用MoveNext并控制工作的完成位置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM