编辑原始问题详细说明:我正在使用Unity协程执行一些重载操作。 Unity是一个游戏引擎,代码在帧内运行。如果操作是密集的,则必须在协程中完成,否则帧需要很长时间才能完成。协程方法是Init
。如果您不熟悉Unity引擎,它们与迭代器类似。
首先,我必须说这些代码可以正常工作。问题在于堆分配。那说我会解释代码的作用。调用DoTask
后,它会启动协程并进入foreach
,然后进入currentTask.Execute()
以迭代obj.CreateCellGosTask
,然后进入public class TaskScheduler : MonoBehaviour
{
private static volatile Task currentTask;
public void Init(){
StartCoroutine(DoTasks()); //Starts the coroutine
}
private IEnumerator DoTasks()
{
while(true){
foreach (object b in currentTask.Execute())
{
yield return b;
//Do something
}
}
}
public class Task
{
private Cell cell;
public IEnumerable Execute()
{
foreach (object b in cell.CreateCellGosTask()){
yield return b;
// Do something
}
}
进行迭代。现在我们遇到的第一个收益率回报,foreachs链将结果返回到初始协程(即StartCoroutine(DoTasks()))并且我们已经完成了框架。在下一帧中,代码在执行的最后一行之后的链中继续。这是行为并且工作正常。
DoTasks
收益率回报的重要性为零。在所有嵌套迭代器中,它都会返回null。
问题在于堆分配。由于编译器生成隐藏的类实现IEnumerable(我认为),因此代码会产生垃圾。不幸的是,垃圾收集在统一中是一个大问题。
最终目标是在foreach链中零堆分配(StartCoroutine并不重要)。那么问题究竟是编译器生成的代码以及它如何创建Enumerable和Enumerator类?我指的是Execute
和{{1}}的确切生成代码。然后我可以输入完全相同的代码并创建并返回 struct 而不是类。
答案 0 :(得分:3)
(您可能更喜欢在下面用大写字母跳过我的简短说明!)
我可能会误解你要做的事情,但是,
1)协同程序与线程完全无关。
(Unity根本不使用线程。如果你需要创建一个线程(比如说是处理)你需要使用一个线程管理器(有很多可用的,或者自己编写)......但是它与协程。)
2)协同程序没有返回值。你完成后只需yield return null
跳过一个框架或中断..
一些说明,
http://answers.unity3d.com/answers/966469/view.html http://answers.unity3d.com/answers/1119978/view.html
这是关于“你怎么称呼''coroutines不止一次'的结果的讨论,这与你所要求的有关。 (当我自己问这个...... https://stackoverflow.com/a/34550206/294884 ......我当然没有意识到这一点时出现了!)
我希望这在某种程度上有所帮助!
最后
4)你不能以任何有意义的方式嵌套协程。
你只是“开始另一个新的协程”。你懂?你所指的只是“等到”一个完成另一个,或者“继续”并一次开始几个。
Google对此进行了100次讨论.. http://answers.unity3d.com/questions/14081/nested-coroutines.html或http://answers.unity3d.com/answers/515074/view.html
你无法以任何方式有意义地“嵌套协程”。
想象一下,你有一张带有挡水板的厨房桌子。你开始跑秒表。如果由于某种原因你想要,你可以开始并运行其中许多。 (他们中的一些人“可能会自己开始其他人”,或者他们可能会从其他地方开始。)
但是没有“嵌套”它们的概念,它们只是在那里运行的秒表。
别忘了,你所说的只是“它是运行每一帧的代码” - 仅此而已。 (完全像Update()
。)
再一次-----我感觉到你真正追求的是Unity中的线程,这可以小心实现。示例---
http://answers.unity3d.com/answers/443604/view.html
事实上,你有点想要与整个帧系统无关,也没有协同作用,听起来像你需要一个线程,或许可以用数学计算。
重复同样的观点,
public class TaskScheduler : MonoBehaviour
请注意协程非常简单
就是这样。如您所知,游戏引擎环境为您提供了“每帧......”概念运行循环。
让我们说无论出于何种原因(比如说......移动一个物体,给怪物制作动画),你想要“每一帧”做一些事情。在Unity中有两种方法可以访问该功能。
(1)只需使用Unity为您提供的Update()quasifunction:
Update()
{
if ( moveTheDinosaur )
{
// code here will run every frame,
// frames are beautifully managed by Unity
{
(2)只需使用协程:
launch coroutine showDinosaur
coroutine showDinosaur()
{
while(true)
{
// code here will run every frame,
// frames are beautifully managed by Unity
yield return null;
// the formulaic line "yield return null"
// indicates to the MonoBehaviour engine that's
// the end of your processing this frame;
// (Implementation details are unknown to us
// and irrelevant)
}
}
请注意 - 事实上 - 如果你是一名经验丰富的程序员,一旦你使用Unity超过一天,你会发现“更新()”的事情通常是完全愚蠢的,你倾向于使用你自己的协程每帧做一些事。 (当然,“Update()”对于快速演示或测试代码时的任何内容都很方便。)
再次强调,couroutines与“任务”或“线程”没有任何联系 - 其中 - 我想,当然我可能是错的 - 是你得到的。协同程序就是你如何访问Unity中的“框架系统”。对于统一线程,请查看许多线程池辅助类型脚本或系统之一,这很方便。