IEnumerator, yeild和协程各自的实现方式,在unity中的联系和应用

很多unity关于协程的教程都集中在讲协程的表象,即:在某某情况下协程怎么实现,有什么作用。 但了解协程的具体实现方法必然可以帮助自己更好的使用这个工具。 

协程的作用: 需要跨越多个帧的“动作”有很多实现的方式,协程提供了一种比较简洁的方式。 即startcorountine(), 在一帧中开始一个“动作“,然后接下来几帧中依然会产生效果。这和另外起一个线程有相似之处。但在游戏中多线程的应用有局限性。原因我猜想如下:游戏需要对每一帧的信息进行计算处理,虽然协程看起来像是把计算放到另一个线程,但实际上引擎依旧需要每一帧都按顺序计算相关信息。 也就是说,把信息保存下来,每一帧都用迭代器去访问需要的信息就是自然的想法了,并没有用多线程。而这种方式相比于多线程会出现的诸多同步问题,优势不言而喻。 


所以协程就是一个迭代器的应用场景。


IEnumerator 

是迭代器。C#1中要继承一个迭代器比较复杂,至少需要实现构造函数,Movenext(),reset(),current属性。 C#2中,可以简化上述步骤,即Movenext(),reset(),current属性已经帮你实现好了,但相应的,你自己就不知道这三个东西的调用时机和实现方式了。简化了的实现方法需要借助yield实现。可以参考这篇博客,讲得很清楚:https://www.cnblogs.com/w-wfy/p/7418459.html。  


yield

 用yield return value, 那么不管value的类型是什么,最后的返回值是 一个类的实例,这个类是编译器帮忙实现了IEnumerator接口的一个类。这个类的形式如下(由反编译而来) :  实现了Movenext(),reset(),current等方法属性。 值得注意的是movenext()的实现方法,直接在一个switch保存了每种状态应该干的事情。简单粗暴。

  1. private sealed class <Power>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable  
  2.         {  
  3.             private int <>1__state;  
  4.             private int <>2__current;  
  5.             public int <>3__exponent;  
  6.             public int <>3__number;  
  7.             private int <>l__initialThreadId;  
  8.             public int <result>5__1;  
  9.             public int exponent;  
  10.             public int number;  
  11.   
  12.             [DebuggerHidden]  
  13.             public <Power>d__0(int <>1__state)  
  14.             {  
  15.                 this.<>1__state = <>1__state;  
  16.                 this.<>l__initialThreadId = Environment.CurrentManagedThreadId;  
  17.             }  
  18.   
  19.             private bool MoveNext()  
  20.             {  
  21.                 switch (this.<>1__state)  
  22.                 {  
  23.                     case 0:  
  24.                         this.<>1__state = -1;  
  25.                         this.<result>5__1 = 1;  
  26.                         Console.WriteLine("Begin to invoke GetItems() method");  
  27.                         this.<>2__current = 3;  
  28.                         this.<>1__state = 1;  
  29.                         return true;  
  30.   
  31.                     case 1:  
  32.                         this.<>1__state = -1;  
  33.                         this.<>2__current = 4;  
  34.                         this.<>1__state = 2;  
  35.                         return true;  
  36.   
  37.                     case 2:  
  38.                         this.<>1__state = -1;  
  39.                         this.<>2__current = 5;  
  40.                         this.<>1__state = 3;  
  41.                         return true;  
  42.   
  43.                     case 3:  
  44.                         this.<>1__state = -1;  
  45.                         break;  
  46.                 }  
  47.                 return false;  
  48.             }  
  49.   
  50.             [DebuggerHidden]  
  51.             IEnumerator<int> IEnumerable<int>.GetEnumerator()  
  52.             {  
  53.                 Program.<Power>d__0 d__;  
  54.                 if ((Environment.CurrentManagedThreadId == this.<>l__initialThreadId) && (this.<>1__state == -2))  
  55.                 {  
  56.                     this.<>1__state = 0;  
  57.                     d__ = this;  
  58.                 }  
  59.                 else  
  60.                 {  
  61.                     d__ = new Program.<Power>d__0(0);  
  62.                 }  
  63.                 d__.number = this.<>3__number;  
  64.                 d__.exponent = this.<>3__exponent;  
  65.                 return d__;  
  66.             }  
  67.   
  68.             [DebuggerHidden]  
  69.             IEnumerator IEnumerable.GetEnumerator()  
  70.             {  
  71.                 return this.System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator();  
  72.             }  
  73.   
  74.             [DebuggerHidden]  
  75.             void IEnumerator.Reset()  
  76.             {  
  77.                 throw new NotSupportedException();  
  78.             }  
  79.   
  80.             void IDisposable.Dispose()  
  81.             {  
  82.             }  
  83.   
  84.             int IEnumerator<int>.Current  
  85.             {  
  86.                 [DebuggerHidden]  
  87.                 get  
  88.                 {  
  89.                     return this.<>2__current;  
  90.                 }  
  91.             }  
  92.   
  93.             object IEnumerator.Current  
  94.             {  
  95.                 [DebuggerHidden]  
  96.                 get  
  97.                 {  
  98.                     return this.<>2__current;  
  99.                 }  
  100.             }  
  101.         }  
  102.     }  
  103. }  

协程:

利用迭代器和yield, Unity 在每一帧都会去处理 GameObject 里带有的 Coroutine Function, 直到 Coroutine Function 被执行完毕. 当一个 Coroutine 开始启动时, 它会执行到遇到 yield 为止, 遇到 yield 的时候 Coroutine 会暂停执行, 直到满足 yield 语句的条件, 会开始执行 yield 语句后面的内容, 直到遇到下一个 yield 为止 ... 如此循环直到整个函数结束, 这就是可以将一个函数分割到多个帧里去执行的思想.

猜你喜欢

转载自blog.csdn.net/qq_30795577/article/details/79932088