Unity協(xié)程(Coroutine)原理深入剖析再續(xù)
?
?By D.S.Qiu
尊重他人的勞動,支持原創(chuàng),轉載請注明出處:http.dsqiu.iteye.com
? ? ? ??
? ? ? ? 前面已經(jīng)介紹過對協(xié)程(Coroutine)的認識和理解,主要講到了Unity引擎在執(zhí)行協(xié)程(Coroutine)的原理(Unity協(xié)程(Coroutine)原理深入剖析)和對協(xié)程(Coroutine)狀態(tài)的控制(Unity協(xié)程(Coroutine)管理類——TaskManager工具分享),到這使用Coroutine的疑問就沒有了,但是D.S.Qiu還是有點沒嚼爛,所以覺得很有必要再續(xù)。
? ? ? ? 本文主要分為三部分:
? ? ? ? ? ? ? ?1)yield return, IEnumerator ?和 Unity StartCoroutine 的關系和理解
? ? ? ? ? ? ???2)Cortoutine 擴展——Extending Coroutines: Return Values and Error Handling
? ? ? ? ? ? ? ?3)Cortountine Locking
?
? ? ? ? ?總之,引用③的一句話:Coroutines – More than you want to know.
? ? ? ? ?
1)yield return, IEnumerator ?和 Unity StartCoroutine 的關系和理解
? ? ? ? ? yield 和 IEnumerator都是C#的東西,前者是一個關鍵字,后者是枚舉類的接口。對于IEnumerator 只引用②對 IEnumerable與IEnumerator區(qū)別 的論述:
先貼出 IEnumerable 和 IEnumerator的定義:
C#代碼??
public?interface?IEnumerable??{??????IEnumerator?GetEnumerator();??}?????public?interface?IEnumerator??{??????bool?MoveNext();??????void?Reset();?????????Object?Current?{?get;?}??}?? ?IEnumerable和IEnumerator有什么區(qū)別?這是一個很讓人困惑的問題(在很多forum里都看到有人在問這個問題)。研究了半天,得到以下幾點認識:
? ? ? ? ?1、一個Collection要支持foreach方式的遍歷,必須實現(xiàn)IEnumerable接口(亦即,必須以某種方式返回IEnumerator object)。
? ? ? ? ?2、IEnumerator object具體實現(xiàn)了iterator(通過MoveNext(),Reset(),Current)。
? ? ? ? ?3、從這兩個接口的用詞選擇上,也可以看出其不同:IEnumerable是一個聲明式的接口,聲明實現(xiàn)該接口的class是“可枚舉(enumerable)”的,但并沒有說明如何實現(xiàn)枚舉器(iterator);IEnumerator是一個實現(xiàn)式的接口,IEnumerator object就是一個iterator。
? ? ? ? ?4、IEnumerable和IEnumerator通過IEnumerable的GetEnumerator()方法建立了連接,client可以通過IEnumerable的GetEnumerator()得到IEnumerator object,在這個意義上,將GetEnumerator()看作IEnumerator object的factory method也未嘗不可。
?
IEnumerator ?是所有枚舉數(shù)的基接口。 ??
? ? ? ? ?枚舉數(shù)只允許讀取集合中的數(shù)據(jù)。枚舉數(shù)無法用于修改基礎集合。 ??
? ? ? ? ?最初,枚舉數(shù)被定位于集合中第一個元素的前面。Reset ? 也將枚舉數(shù)返回到此位置。在此位置,調用 ? Current ? 會引發(fā)異常。因此,在讀取 ? Current ? 的值之前,必須調用 ? MoveNext ? 將枚舉數(shù)提前到集合的第一個元素。 ??
? ? ? ? ?在調用 ? MoveNext ? 或 ? Reset ? 之前,Current ? 返回同一對象。MoveNext ? 將 ? Current ? 設置為下一個元素。 ??
? ? ? ? ?在傳遞到集合的末尾之后,枚舉數(shù)放在集合中最后一個元素后面,且調用 ? MoveNext ? 會返回 ? false。如果最后一次調用 ? MoveNext ? 返回 ? false,則調用 ? Current ? 會引發(fā)異常。若要再次將 ? Current ? 設置為集合的第一個元素,可以調用 ? Reset,然后再調用 ? MoveNext。 ??
? ? ? ? ?只要集合保持不變,枚舉數(shù)就將保持有效。如果對集合進行了更改(例如添加、修改或刪除元素),則該枚舉數(shù)將失效且不可恢復,并且下一次對 ? MoveNext ? 或 ? Reset ? 的調用將引發(fā) ? InvalidOperationException。如果在 ? MoveNext ? 和 ? Current ? 之間修改集合,那么即使枚舉數(shù)已經(jīng)無效,Current ? 也將返回它所設置成的元素。 ??
? ? ? ? ?枚舉數(shù)沒有對集合的獨占訪問權;因此,枚舉一個集合在本質上不是一個線程安全的過程。甚至在對集合進行同步處理時,其他線程仍可以修改該集合,這會導致枚舉數(shù)引發(fā)異常。若要在枚舉過程中保證線程安全,可以在整個枚舉過程中鎖定集合,或者捕捉由于其他線程進行的更改而引發(fā)的異常。
?
Yield關鍵字
在迭代器塊中用于向枚舉數(shù)對象提供值或發(fā)出迭代結束信號。它的形式為下列之一⑥:
yield return <expression_r>;
yield break;
備注 :
計算表達式并以枚舉數(shù)對象值的形式返回;expression_r 必須可以隱式轉換為迭代器的 yield 類型。
yield 語句只能出現(xiàn)在 iterator 塊中,該塊可用作方法、運算符或訪問器的體。這類方法、運算符或訪問器的體受以下約束的控制:
不允許不安全塊。
方法、運算符或訪問器的參數(shù)不能是 ref 或 out。
yield 語句不能出現(xiàn)在匿名方法中。
當和 expression_r 一起使用時,yield return 語句不能出現(xiàn)在 catch 塊中或含有一個或多個 catch 子句的 try 塊中。
?
yield return 提供了迭代器一個比較重要的功能,即取到一個數(shù)據(jù)后馬上返回該數(shù)據(jù),不需要全部數(shù)據(jù)裝入數(shù)列完畢,這樣有效提高了遍歷效率。
?
Unity StartCoroutine
? ? ? Unity使用 StartCoroutine(routine: IEnumerator): Coroutine 啟動協(xié)程,參數(shù)必須是 IEnumerator 對象。那么Unity在背后做什么神奇的處理呢?
? ? ? StartCoroutine函數(shù)的參數(shù)我一般都是通過傳入一個返回值為 IEnumerator的函數(shù)得到的:
C#代碼??
IEnumerator?WaitAndPrint(float?waitTime)?{??????yield?return?new?WaitForSeconds(waitTime);??????print("WaitAndPrint?"?+?Time.time);??}?? ? ? ? ?在函數(shù)內使用前面介紹?yield 關鍵字返回 IEnumerator 對象,Unity 中實現(xiàn)了 YieldInstruction 作為 yield 返回的基類,有 Cortoutine, WaitForSecondes, WaitForEndOfFrame, WaitForFixedUpdate, WWW 幾個子類實現(xiàn)。StartCoroutine 將 傳入的 IEnumerator 封裝為 Coroutine 返回,引擎會對 Corountines 存儲和檢查 IEnumerator 的 Current值。
?
③枚舉了 WWW ,WaitForSeconds , null 和 WaitForEndOfFrame 檢查 Current值在MonoBebaviour生存周期的時間(沒有WaitForFixedUpdate ,D.S.Qiu猜測是其作者成文是Unity引擎還沒有提供這個實現(xiàn)):
?
? ? ? ?WWW - after Updates happen for all game objects; check the isDone flag. If true, call the IEnumerator's MoveNext() function;
? ? ? ?WaitForSeconds - after Updates happen for all game objects; check if the time has elapsed, if it has, call MoveNext();
? ? ? ?null or some unknown value - after Updates happen for all game objects; Call MoveNext();
? ? ? ?WaitForEndOfFrame - after Render happens for all cameras; Call MoveNext().
?
如果最后一個 yield return 的 IEnumerator 已經(jīng)迭代到最后一個是,MoveNext 就會 返回 false 。這時,Unity就會將這個 IEnumerator 從 cortoutines list 中移除。
?
? ? ? ?所以很容易一個出現(xiàn)的誤解:協(xié)程 Coroutines 并不是并行的,它和你的其他代碼都運行在同一個線程中,所以才會在Update 和 Coroutine中使用 同一個值時才會變得線程安全。這就是Unity對線程安全的解決策略——直接不使用線程,最近Unity 5 將要發(fā)布說的很熱,看到就有完全多線程的支持,不知道是怎么實現(xiàn)的,從技術的角度,還是很期待的哈。
?
? ? ? ?總結下: 在協(xié)程方法中使用 yield return 其實就是為了返回 IEnumerator對象,只有當這個對象的 MoveNext() 返回 false 時,即該 IEnumertator 的 Current 已經(jīng)迭代到最后一個元素了,才會執(zhí)行 yield return 后面的語句。也就是說, yield return 被會“翻譯”為一個 IEnmerator 對象,要想深入了解這方面的更多細節(jié),可以猛擊⑤查看。
? ? ? ?根據(jù)⑤ C# in depth 的理解——C# 編譯器會生成一個 IEnumerator 對象,這個對象實現(xiàn)的 MoveNext() 包含函數(shù)內所有 yield return 的處理,這里僅附上一個例子:
C#代碼??
using?System;??using?System.Collections;????class?Test??{??????static?IEnumerator?GetCounter()??????{??????????for?(int?count?=?0;?count?<?10;?count++)??????????{??????????????yield?return?count;??????????}??????}??}?? ?C#編譯器對應生成:
Cpp代碼??
internal?class?Test??{??????????private?static?IEnumerator?GetCounter()??????{??????????return?new?<GetCounter>d__0(0);??????}????????????[CompilerGenerated]??????private?sealed?class?<GetCounter>d__0?:?IEnumerator<object>,?IEnumerator,?IDisposable??????{??????????????????????????private?int?<>1__state;??????????private?object?<>2__current;??????????public?int?<count>5__1;????????????[DebuggerHidden]??????????public?<GetCounter>d__0(int?<>1__state)??????????{??????????????this.<>1__state?=?<>1__state;??????????}????????????????????private?bool?MoveNext()??????????{??????????????switch?(this.<>1__state)??????????????{??????????????????case?0:??????????????????????this.<>1__state?=?-1;??????????????????????this.<count>5__1?=?0;??????????????????????while?(this.<count>5__1?<?10)????????????????????????????{??????????????????????????this.<>2__current?=?this.<count>5__1;??????????????????????????this.<>1__state?=?1;??????????????????????????return?true;??????????????????????Label_004B:??????????????????????????this.<>1__state?=?-1;??????????????????????????this.<count>5__1++;??????????????????????}??????????????????????break;????????????????????case?1:??????????????????????goto?Label_004B;??????????????}??????????????return?false;??????????}????????????[DebuggerHidden]??????????void?IEnumerator.Reset()??????????{??????????????throw?new?NotSupportedException();??????????}????????????void?IDisposable.Dispose()??????????{??????????}????????????object?IEnumerator<object>.Current??????????{??????????????[DebuggerHidden]??????????????get??????????????{??????????????????return?this.<>2__current;??????????????}??????????}????????????object?IEnumerator.Current??????????{??????????????[DebuggerHidden]??????????????get??????????????{??????????????????return?this.<>2__current;??????????????}??????????}??????}??}?? ? ? ? ?從上面的C#實現(xiàn)可以知道:函數(shù)內有多少個 yield return 在對應的 MoveNext() 就會返回多少次 true (不包含嵌套)。另外非常重要的一點的是:同一個函數(shù)內的其他代碼(不是 yield return 語句)會被移到 MoveNext 中去,也就是說,每次 MoveNext 都會順帶執(zhí)行同一個函數(shù)中 yield return 之前,之后 和兩個 yield return 之間的代碼。
? ? ? ?對于Unity 引擎的 YieldInstruction 實現(xiàn),其實就可以看著一個 函數(shù)體,這個函數(shù)體每幀會實現(xiàn)去 check MoveNext 是否返回 false 。 例如:
C#代碼??
yield?retrun?new?WaitForSeconds(2f);?? ? ? ? ?上面這行代碼的偽代碼實現(xiàn):
C#代碼??
private?float?elapsedTime;??private?float?time;????private?void?MoveNext()??{??????????elapesedTime?+=?Time.deltaTime;????????????????????if(time?<=?elapsedTime)??????????????????return?false;??????????else?return?true;????}?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?增補于: 2014年04月22日 8:00
?
?2)Cortoutine 擴展——Extending Coroutines: Return Values and Error Handling
? ? ? ? 不知道你們調用 StartCortoutine 的時候有沒有注意到 StartCortoutine 返回了 YieldInstruction 的子類 Cortoutine 對象,這個返回除了嵌套使用 StartCortoutine 在 yiled retrun StartCortoutine 有用到,其他情況機會就沒有考慮它的存在,反正D.S.Qiu是這樣的,一直認為物“極”所用,所以每次調用 StartCortoutine 都很糾結,好吧,有點強迫癥。
? ? ? ?Unity引擎講 StartCoroutine 傳入的參數(shù) IEnumerator 封裝為一個 Coroutine 對象中,而 Coroutine 對象其實也是 IEnumerator 枚舉對象。yield return 的 IEnumerator 對象都存儲在這個 Coroutine 中,只有當上一個yield return 的 IEnumerator 迭代完成,才會運行下一個。這個在猜測下Unity底層對Cortountine 的統(tǒng)一管理(也就是上面說的檢查 Current 值):Unity底層應該有一個 正在運行的 Cortoutine 的 list 然后在每幀的不同時間去 Check。
?
? ? ? ? 還是回歸到主題,上面介紹 yield 關鍵字有說不允許不安全塊,也就是說不能出現(xiàn)在 try catch 塊中,就不能在 yield return 執(zhí)行是進行錯誤檢查。③利用 StartCortoutine 返回值 Cortoutine 得到了當前的 Current 值和進行錯誤捕獲處理。
先定義封裝包裹返回值和錯誤信息的類:
C#代碼??
public?class?Coroutine<T>{??public?T?Value?{??????get{??????????if(e?!=?null){??????????????throw?e;??????????}??????????return?returnVal;??????}??}??private?T?returnVal;??private?Exception?e;????public?Coroutine?coroutine;????public?IEnumerator?InternalRoutine(IEnumerator?coroutine){??????????}??}?? ?InteralRoutine是對返回 Current 值和拋出的異常信息(如果有的話):
C#代碼??
public?IEnumerator?InternalRoutine(IEnumerator?coroutine){??????while(true){??????????try{??????????????if(!coroutine.MoveNext()){??????????????????yield?break;??????????????}??????????}??????????catch(Exception?e){??????????????this.e?=?e;??????????????yield?break;??????????}??????????object?yielded?=?coroutine.Current;??????????if(yielded?!=?null?&&?yielded.GetType()?==?typeof(T)){??????????????returnVal?=?(T)yielded;??????????????yield?break;??????????}??????????else{??????????????yield?return?coroutine.Current;??????????}??????}?? ?下面為這個類擴展MonoBehavior:
C#代碼??
public?static?class?MonoBehaviorExt{??????public?static?Coroutine<T>?StartCoroutine<T>(this?MonoBehaviour?obj,?IEnumerator?coroutine){??????????Coroutine<T>?coroutineObject?=?new?Coroutine<T>();??????????coroutineObject.coroutine?=?obj.StartCoroutine(coroutineObject.InternalRoutine(coroutine));??????????return?coroutineObject;??????}??}?? ?最后給出一個 Example:
C#代碼??
IEnumerator?Start?()?{??????????var?routine?=?StartCoroutine<int>(TestNewRoutine());?????????yield?return?routine.coroutine;?????????Debug.Log(routine.Value);?????}????????????IEnumerator?TestNewRoutine(){??????????yield?return?null;??????????yield?return?new?WaitForSeconds(2f);??????????yield?return?10;??????????????????yield?return?5;??????}?? ?最后輸出是10,因為Cortoutine<T> 遇到滿足條件的 T 類型就 執(zhí)行 yield break;就不執(zhí)行 yield return 5; 這條語句了。
如果將中 yield break; 語句去掉的話,最后輸出的是 5 而不是10。
C#代碼??
if(yielded?!=?null?&&?yielded.GetType()?==?typeof(T)){??????????????returnVal?=?(T)yielded;??????????????yield?break;??????????}??
其實就是Unity引擎每幀去 check yield return 后面的表達式,如果滿足就繼續(xù)向下執(zhí)行。
?
下面在測試一個例子:連續(xù)兩次調用 yield return coroutine;
C#代碼??
?private?Coroutine?routine1;???void?Start?()????{??????routine1?=?StartCoroutine(TestCoroutineExtention1());?????StartCoroutine(TestCortoutine());????????}??IEnumerator?TestCoroutineExtention1()??{??????yield?return?new?WaitForSeconds(1);??????yield?return?10;??????Debug.Log("Run?10!");??????yield?return?new?WaitForSeconds(5);??????yield?return?5;??????Debug.Log("Run?5!");??}??IEnumerator?TestCortoutine()??{??????????yield?return?routine1;?????Debug.Log("?routine1");??????yield?return?routine1;?????Debug.Log("?routine2");??}?? ?測試運行會發(fā)現(xiàn)只會輸出:
? ? ? ? Run 10!
? ? ? ? Run 5!
? ? ? ? ?routine1
?
總結下: yield return expression 只有表達式完全執(zhí)行結束才會繼續(xù)執(zhí)行后面的代碼,連續(xù)兩次執(zhí)行 yield return StartCortoutine() 的返回值是不會滿足的,說明 yield return 有區(qū)分開始和結束的兩種狀態(tài)。
?
?
3)Cortoutine Locking
? ? ? ? ? 雖然Cortoutine不是多線程機制,但仍會“并發(fā)”問題——同時多次調用 StartCortoutine ,當然通過Unity提供的api也能得到解決方案,每次StartCoroutine 之前先調用 StopCortoutine 方法停止,但這利用的是反射,顯然效率不好。④對③的方案進行了擴展提供了 Cortoutine Locking 的支持,使用字符串(方法名)來標記同一個 Coroutine 方法,對于同一個方法如果等待時間超過 timeout 就會終止前面一個 Coroutine 方法,下面直接貼出代碼:
C#代碼??
using?UnityEngine;??using?System;??using?System.Collections;??using?System.Collections.Generic;????public?class?TTMonoBehaviour?:?MonoBehaviour??{??????private?LockQueue?LockedCoroutineQueue?{?get;?set;?}????????????????????????????????public?Coroutine<T>?StartCoroutine<T>(IEnumerator?coroutine)??????{??????????Coroutine<T>?coroutineObj?=?new?Coroutine<T>();??????????coroutineObj.coroutine?=?base.StartCoroutine(coroutineObj.InternalRoutine(coroutine));??????????return?coroutineObj;??????}????????????????????????????????public?Coroutine<T>?StartCoroutine<T>(IEnumerator?coroutine,?string?lockID,?float?waitTime?=?10f)??????{??????????if?(LockedCoroutineQueue?==?null)?LockedCoroutineQueue?=?new?LockQueue();??????????Coroutine<T>?coroutineObj?=?new?Coroutine<T>(lockID,?waitTime,?LockedCoroutineQueue);??????????coroutineObj.coroutine?=?base.StartCoroutine(coroutineObj.InternalRoutine(coroutine));??????????return?coroutineObj;??????}????????????????????????public?class?Coroutine<T>??????{??????????private?T?returnVal;??????????private?Exception?e;??????????private?string?lockID;??????????private?float?waitTime;????????????????????private?LockQueue?lockedCoroutines;?????????private?bool?lockable;????????????????????public?Coroutine?coroutine;??????????public?T?Value??????????{??????????????get???????????????{???????????????????if?(e?!=?null)??????????????????{??????????????????????throw?e;??????????????????}??????????????????return?returnVal;??????????????}??????????}????????????????????public?Coroutine()?{?lockable?=?false;?}??????????public?Coroutine(string?lockID,?float?waitTime,?LockQueue?lockedCoroutines)??????????{??????????????this.lockable?=?true;??????????????this.lockID?=?lockID;??????????????this.lockedCoroutines?=?lockedCoroutines;??????????????this.waitTime?=?waitTime;??????????}????????????????????public?IEnumerator?InternalRoutine(IEnumerator?coroutine)??????????{??????????????if?(lockable?&&?lockedCoroutines?!=?null)??????????????{?????????????????????????if?(lockedCoroutines.Contains(lockID))??????????????????{??????????????????????if?(waitTime?==?0f)??????????????????????{??????????????????????????????????????????????????yield?break;??????????????????????}??????????????????????else??????????????????????{??????????????????????????????????????????????????float?starttime?=?Time.time;??????????????????????????float?counter?=?0f;??????????????????????????lockedCoroutines.Add(lockID,?coroutine);??????????????????????????while?(!lockedCoroutines.First(lockID,?coroutine)?&&?(Time.time?-?starttime)?<?waitTime)??????????????????????????{??????????????????????????????yield?return?null;??????????????????????????????counter?+=?Time.deltaTime;??????????????????????????}??????????????????????????if?(counter?>=?waitTime)??????????????????????????{???????????????????????????????string?error?=?this.GetType().Name?+?":?coroutine?"?+?lockID?+?"?bailing!?due?to?timeout:?"?+?counter;??????????????????????????????Debug.LogError(error);??????????????????????????????this.e?=?new?Exception(error);??????????????????????????????lockedCoroutines.Remove(lockID,?coroutine);??????????????????????????????yield?break;??????????????????????????}??????????????????????}??????????????????}??????????????????else??????????????????{??????????????????????lockedCoroutines.Add(lockID,?coroutine);??????????????????}??????????????}????????????????????????????while?(true)??????????????{??????????????????try???????????????????{??????????????????????if?(!coroutine.MoveNext())??????????????????????{??????????????????????????if?(lockable)?lockedCoroutines.Remove(lockID,?coroutine);??????????????????????????yield?break;??????????????????????}??????????????????}??????????????????catch?(Exception?e)??????????????????{??????????????????????this.e?=?e;??????????????????????Debug.LogError(this.GetType().Name?+?":?caught?Coroutine?exception!?"?+?e.Message?+?"\n"?+?e.StackTrace);???????????????????????if?(lockable)?lockedCoroutines.Remove(lockID,?coroutine);??????????????????????yield?break;??????????????????}????????????????????????????????????object?yielded?=?coroutine.Current;??????????????????if?(yielded?!=?null?&&?yielded.GetType()?==?typeof(T))??????????????????{??????????????????????returnVal?=?(T)yielded;??????????????????????if?(lockable)?lockedCoroutines.Remove(lockID,?coroutine);??????????????????????yield?break;??????????????????}??????????????????else??????????????????{??????????????????????yield?return?coroutine.Current;??????????????????}??????????????}??????????}??????}??????????????????????????????public?class?LockQueue??????{??????????private?Dictionary<string,?List<IEnumerator>>?LockedCoroutines?{?get;?set;?}????????????????????public?LockQueue()??????????{??????????????LockedCoroutines?=?new?Dictionary<string,?List<IEnumerator>>();??????????}????????????????????????????????????????????public?bool?Contains(string?lockID)??????????{??????????????return?LockedCoroutines.ContainsKey(lockID);??????????}????????????????????????????????????????????public?bool?First(string?lockID,?IEnumerator?coroutine)??????????{??????????????bool?ret?=?false;??????????????if?(Contains(lockID))??????????????{??????????????????if?(LockedCoroutines[lockID].Count?>?0)??????????????????{??????????????????????ret?=?LockedCoroutines[lockID][0]?==?coroutine;??????????????????}??????????????}??????????????return?ret;??????????}????????????????????????????????????????????public?void?Add(string?lockID,?IEnumerator?coroutine)??????????{??????????????if?(!LockedCoroutines.ContainsKey(lockID))??????????????{??????????????????LockedCoroutines.Add(lockID,?new?List<IEnumerator>());??????????????}????????????????????????????if?(!LockedCoroutines[lockID].Contains(coroutine))??????????????{??????????????????LockedCoroutines[lockID].Add(coroutine);??????????????}??????????}????????????????????????????????????????????public?bool?Remove(string?lockID,?IEnumerator?coroutine)??????????{??????????????bool?ret?=?false;??????????????if?(LockedCoroutines.ContainsKey(lockID))??????????????{??????????????????if?(LockedCoroutines[lockID].Contains(coroutine))??????????????????{??????????????????????ret?=?LockedCoroutines[lockID].Remove(coroutine);??????????????????}????????????????????????????????????if?(LockedCoroutines[lockID].Count?==?0)??????????????????{??????????????????????ret?=?LockedCoroutines.Remove(lockID);??????????????????}??????????????}??????????????return?ret;??????????}????????????????}????}?? ?
小結:
? ? ? ?本文主要是對 Unity StartCoroutine 進行了理解,從C# 的yileld 和 IEnumerator 到 Unity 的 StartCoroutine,最后并對Cortoutine 進行了擴展,雖然感覺不是很實用(用到的情況非常至少),但還是有利于對Coroutine 的理解和思考。
? ? ? ?對于第三部分的代碼感覺有不妥,沒有進行測試,附件里有代碼,有需求的話請自取
?
? ? ? ? 如果您對D.S.Qiu有任何建議或意見可以在文章后面評論,或者發(fā)郵件(gd.s.qiu@gmail.com)交流,您的鼓勵和支持是我前進的動力,希望能有更多更好的分享。
? ? ? ?轉載請在文首注明出處:http://dsqiu.iteye.com/blog/2049743
?
更多精彩請關注D.S.Qiu的博客和微博(ID:靜水逐風)?
?
?
?
參考:
①琪琪爸的程序學習筆記 :-P:http://www.cnblogs.com/easyfrog/archive/2011/12/29/IEnumerable_IEnumerator_yield.html
②杰仔:http://www.cnblogs.com/illele/archive/2008/04/21/1164696.html
③Twisted Oak Studios:?http://twistedoakstudios.com/blog/Post83_coroutines-more-than-you-want-to-know
④tim tregubov:http://zingweb.com/blog/2013/02/05/unity-coroutine-wrapper/
⑤C# in Depth:?http://csharpindepth.com/articles/chapter6/iteratorblockimplementation.aspx
⑥zhw1125:?http://blog.sina.com.cn/s/blog_3e29b20b0100g6ix.html
?
轉載于:https://www.cnblogs.com/Uinkanade/articles/3984676.html
總結
以上是生活随笔為你收集整理的Unity协程(Coroutine)原理深入剖析再续的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。