一、协程是什么
协程不是进程,更不是线程
协程是在主线程中执行的
协程大概的意思是:
协程函数中暂时不执行 yield 后边的代码,返回到调用协程函数的位置继续执行,
当满足 yield 的条件后再执行后边的代码。
协程能当延时器使用,当你理解它的运行逻辑后,也许你能开发出更多、更有效的用法。
二、协程的用法
1、定义的协程函数必须是 IEnumerator 返回类型
2、在协程函数里边至少有一个 yield 语句
3、yield 后边可以有多种条件
4、调用协程函数,必须使用 StartCoroutine() 方法
StartCoroutine(Test()); //调用
IEnumerator Test() {
//code1
yield return null; //这里的意思是下一帧再继续执行
yield return new WaitForSeconds(2); //这里的意思是等待两秒再执行
yield return 其他条件都可; //虽然其他条件表达式都可以,
//但是任何真假表达式它都是等待一帧再执行。
//code2
}
其他用法详见:C#协程的用法
三、协程的实验
1、使用 yield return null,并在 Start() 方法中调用;
message.Enqueue(FPSCount.ToString() + ": yield before"); yield return null; message.Enqueue(FPSCount.ToString() + ": yield after");
可以看到,在第0帧的时候,输出了 before
第2帧输出了after
与预期的不太一样呢,应该间隔一帧
原因是,在Start中调用时,Update还未执行,所以第一帧计数就没有增加
然后就没有输出了,可以知道,这个协程函数只经历了一次函数周期
2、使用 yield return new WaitForSeconds(2); 在Start中
message.Enqueue(FPSCount.ToString() + ": yield before"); yield return new WaitForSeconds(2); message.Enqueue(FPSCount.ToString() + ": yield after");
输出和上图一致,只不过输出after时,间隔了两秒
3、使用 yield return 条件表达式
message.Enqueue(FPSCount.ToString() + ": yield before"); yield return false; //yield return true; message.Enqueue(FPSCount.ToString() + ": yield after");
在Update中;
在Start中message.Enqueue(FPSCount.ToString() + ": yield before"); yield return false; //yield return true; message.Enqueue(FPSCount.ToString() + ": yield after"); StartCoroutine(test()); //加这一句为了抑制调用协程函数
运行出来发现,在Update中,before和after在同一帧,注意,before在after前面。并且,它不关心return后边是真是假,依旧是下一帧执行,以及有文章说yield return num; 为间隔num帧执行,而我测试依旧是间隔一帧,不知道是不是我的写法有问题。
在Start中,before和after不在同一帧。
这里的细节关系到下面讲的,协程运行原理中的协程执行顺序。
这两个例子都会连续每帧输出,原因是,Update会每帧执行,执行时就会每帧都调用协程函数。所以协程函数并不是循环执行的,它只执行一个函数周期,当执行完成后,函数应该就销毁了,也就是yield后的条件满足后并执行完后边代码就销毁了。
而第二个在Start中,只执行了一次,但是,我在最后边加了再次调用本协程函数的方法,所以他会是一个死循环得递归。但是值得注意的是,它并不会卡死程序,因为他在一帧只递归一次。
四、协程的原理(个人理解)
我们都知道,在U3D中,一个脚本都有一个调用顺序,Start在脚本挂起时,只调用一次就不调用了,Update在每帧调用,FixedUpdate每隔0.02秒调用一次,其他的暂不研究。
那么Update和协程有什么联系呢?
协程yield后的代码会在满足条件后执行,所以每一帧都会检测协程的条件,判断它是否满足。
通过上面第3个实验,我推理出了协程可能会在哪里去检测。见下图:
首先执行Start,接着执行Update,协程检测应该在Update的后边。
推理方法为假设法
假设协程检测在Update之前:
那么在第一帧,先会检测是否有满足的协程条件,
此时我们还没有开启协程,自然没有任何东西,
然后执行到update,运行到了协程函数,输出了before,并退出,下面的下一帧再执行,
然后新的一帧,检测了协程条件,发现上一帧有个未完成的协程函数,并接着执行,输出after,
然后执行update,又运行协程函数,输出before,并把后面的交给下一帧。
此时帧数和输出对应的是
1:before
2:after
2:before
3:after
3:before
显然与我们实验的不符,见实验3-Update部分,正确的应该是在同一帧,before在前。
接着假设协程检测在Update后
第一帧先执行update,并在内执行协程函数,输出before,
update完后检测协程条件,发现有协程事件,但条件不满足,因为当前在同一帧,
在第二帧,先执行update,执行协程函数,输出before,
完后检测,发现有来个协程事件,其中,满足上一帧的一个事件,则输出after,
以此类推,此时帧数和输出对应的是
1:before
2:before
2:after
3:before
3:after
此时在同一帧,before在after前,符合输出。
以上均为个人理解,不能作为专业解释。