1.协程的定义:协同程序,在主线程运行的同时,开启另外一段逻辑处理,来协同当前主线程的执行。协程不是多线程!!!
下面来看例子中的详细解释:从打印结果来分析协同和主线程的关系!!!!
void Start()
{
Debug.Log(" 001");
this.StartCoroutine("Test01");
Debug.Log(" 002");
}
IEnumerator Test01()
{
float timeNum = 0f;
Debug.Log(" time=" + Time.time);
yield return new WaitForSeconds(2);
Debug.Log(" time=" + Time.time);
for (int i = 0; i < 10; i++)
{
Debug.Log(" time=" + Time.time + " i=" + i);
yield return i;
}
// 等待物理更新完毕(所有脚本中的FixedUpdate()函数都被执行后从当前位置继续执行)
yield return new WaitForFixedUpdate();
// 等待渲染帧完毕(在所有的渲染以及GUI程序执行完成后从当前位置继续执行)
yield return new WaitForEndOfFrame();
// 等待一帧 在fixedupdate后执行
yield return null;
}
打印结果顺序:001 -> time=0 -> 002 .
在time=0后面执行 yield return new WaitForSeconds(2); 此时就把执行逻辑交还给主线程了所以才会先打印 002 ,协程中等待2秒后在执行 打印 time=…
2.协程的使用和暂停
StartCoroutine(“方法名”) 或者 StopCoroutine(methodName)
StartCoroutine(“Test01”); StopCoroutine(Test01())
下面详细来说一下协程背后的:迭代器
void Test02() {
List<string> st = new List<string>();
st.Add("a");
st.Add("b");
st.Add("c");
st.Add("d");
st.Add("e");
st.Add("f");
Debug.Log("----分界线----方法(2)和 方法(1)效果相等--");
// 方法(2)和 方法(1)效果相等
// 方法(1)
var ie = st.GetEnumerator();
while (ie.MoveNext())
{
var cur = ie.Current;
Debug.Log(" cur ="+ cur);
}
//方法(2)
foreach (var item in st)
{
Debug.Log(" cur =" + item);
}
///-----------------------------------------
Debug.Log("----分界线----方法(3)和 方法(4)效果相等--");
// 方法(3)和 方法(4)效果相等
// 方法(3)
foreach (var item in test03())
{
Debug.Log("cur = " + item);
}
// 方法(4)
IEnumerator ieTest03 = test03().GetEnumerator();
while (ie.MoveNext())
{
var cur = ieTest03.Current;
Debug.Log(" cur =" + cur);
}
}
IEnumerable<string> test03()
{
for (int i = 0; i < 10; i++)
{
yield return i.ToString();
}
yield return "";
}
反编译后查看:
[CompilerGenerated]
private sealed class <Power>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable
{
private int <>1__state;
private int <>2__current;
public int <>3__exponent;
public int <>3__number;
public string <>3__s;
private int <>l__initialThreadId;
public int <i>5__2;
public int <result>5__1;
public int exponent;
public int number;
public string s;
[DebuggerHidden]
public <Power>d__0(int <>1__state)
{
this.<>1__state = <>1__state;
this.<>l__initialThreadId = Environment.CurrentManagedThreadId;
}
private bool MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<result>5__1 = 1;
if (string.IsNullOrEmpty(this.s))
{
Console.WriteLine("Begin to invoke GetItems() method");
}
this.<i>5__2 = 0;
while (this.<i>5__2 < this.exponent)
{
this.<result>5__1 *= this.number;
this.<>2__current = this.<result>5__1;
this.<>1__state = 1;
return true;
Label_009D:
this.<>1__state = -1;
this.<i>5__2++;
}
this.<>2__current = 3;
this.<>1__state = 2;
return true;
case 1:
goto Label_009D;
case 2:
this.<>1__state = -1;
this.<>2__current = 4;
this.<>1__state = 3;
return true;
case 3:
this.<>1__state = -1;
this.<>2__current = 5;
this.<>1__state = 4;
return true;
case 4:
this.<>1__state = -1;
break;
}
return false;
}
[DebuggerHidden]
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
Program.<Power>d__0 d__;
if ((Environment.CurrentManagedThreadId == this.<>l__initialThreadId) && (this.<>1__state == -2))
{
this.<>1__state = 0;
d__ = this;
}
else
{
d__ = new Program.<Power>d__0(0);
}
d__.number = this.<>3__number;
d__.exponent = this.<>3__exponent;
d__.s = this.<>3__s;
return d__;
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return this.System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
void IDisposable.Dispose()
{
}
int IEnumerator<int>.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
}
反编译的代码就不详细讲了,下面用一个例子来说明***状态的切换***:
(1)在调用一个使用了迭代器块,返回类型为一个 IEnumerable 或者 IEnumerable接口的方法时,这个方法并非立刻执行我们自己写的代码
(2)而是先创建一个编译器生成的类的实例,然后调用MoveNext方法时(如果返回值是IEnumerable 则要先调用GetEnumerator),我们的代码才开始执行,直到遇到第一个yield return 或 yield break语句,此时返回一个bool值来判断迭代是否结束
(3)当下次再调用MoveNext方法时,我们的方法会继续从上一个yield return语句处开始执行。
测试代码如下:
void Test04()
{
Debug.Log("---调用TestStateChange ");
IEnumerable<int>iteratorable = TestStateChange();
Debug.Log("---调用GetEnumerator ");
IEnumerator<int> iterator = iteratorable.GetEnumerator();
Debug.Log("---调用MoveNext ");
bool hasNext = iterator.MoveNext();
Debug.Log($" 是否有数据 = {hasNext},current={iterator.Current}");
Debug.Log("第二次调用 moveNext");
hasNext = iterator.MoveNext();
Debug.Log($" 是否有数据 = {hasNext},current={iterator.Current}");
Debug.Log("第三次调用 moveNext");
hasNext = iterator.MoveNext();
Debug.Log($" 是否有数据 = {hasNext}");
}
IEnumerable<int> TestStateChange()
{
Debug.Log("---我TestStateChange 是第一行代码 ");
Debug.Log("---我是第一个yield return 前的代码 ");
yield return 1;
Debug.Log("---我是第一个yield return 后的代码 ");
Debug.Log("---我是第二个yield return 前的代码 ");
yield return 2;
Debug.Log("---我是第二个yield return 后的代码 ");
}
通过以上结果,可以清晰的看到 movenext 和 yield return,以及状态时如何切换的:
第一次moveNext发生再GetEnumerator方法之后,此时的状态就已经发生了改变。