c# - 协程是Unity3D中的新线程吗?

标签 c# unity3d coroutine

我对coroutines如何感到困惑和好奇(在 Unity3D 和其他地方)工作。协程是新线程吗? Unity的documentation他们说:

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

他们有 C# 示例 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);
    }
}

我对这个例子有很多疑问:

  1. 在上面的例子中,哪一行是协程? WaitAndPrint() 是协程吗? WaitForSeconds() 是协程吗?

  2. 在这一行中:yield return new WaitForSeconds(waitTime);,为什么 yieldreturn 都存在?我读了 Unity documentation “yield 语句是一种特殊的返回,它确保函数在下次调用时从 yield 语句之后的行继续。”如果yield是一个特殊的return,那么return在这里做什么?

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

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

  5. WaitAndPrint() 在上面的例子中被调用了多少次? yield return new WaitForSeconds(waitTime); 真的返回了吗?如果是,那么我猜 WaitAndPrint() 在上面的代码中被调用了两次。我猜 StartCoroutine() 多次调用了 WaitAndPrint()。但是,我看到了another Unity documentation上面写着:“可以使用 yield 语句随时暂停协程的执行。yield 返回值指定何时恢复协程。”这句话让我觉得WaitAndPrint()其实还没有返回;它只是暂停了;它正在等待 WaitForSeconds() 返回。如果是这样的话,那么上面的代码中WaitAndPrint()只被调用了一次,而StartCoroutine只是负责启动函数,并没有多次调用。

最佳答案

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

Microsoft CCR (读一读)也采用(采用?)这种方法。

让我们先解决一件事。单独使用 yield 是无效的,它后面始终跟有 returnbreak

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

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

现在:

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

输出是什么?

hello
foo!
world

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

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

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

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

关于c# - 协程是Unity3D中的新线程吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16951904/

相关文章:

c# - 如何在 Unity Test Runner 中测试 Unity 协程

kotlin - 为什么 operation.map(launch thread).foreach(join()) 在 kotlin 中工作?

c# - 解码 LZMA 流 c#

c# - 使用 C# 和 Unity 3D 在 Leap Motion 中启用手势

c# - 在 unity c# 中更改运行时的动画速度

android - ARCore:模拟器和统一

c++ - C++ 20中协程的机制是什么?

c# - 如何保存具有多个缩进设置的 XmlDocument?

c# - 如何在子文件夹中组织 C# 源文件?

c# - 在 C# 中处理 HttpPostedFile