最近看到Unity3D中的Coroutine,思路相當值得借鑒。
在伺服器(尤其是基於事件驅動架構的伺服器)編程中,非同步作業是難以避免的。傳統的非同步作業會因為大量的回調操作造成邏輯的支離破碎,再加上狀態維護和各種異常處理,很容易就將代碼的複雜度提升到外星人才能理解的程度。
比如,有這樣一個寵物AI:
等待主人下達移動命令;
移動到目標地點;
等待3秒鐘;
喊話;
常用的編程模式(非輪詢):
class Pet<br />{<br /> public event MoveCompleted;<br /> public DispatcherTimer timer = new DispatcherTimer;<br /> // 處理主人命令移動的訊息<br /> [MessageHandler]<br /> public void HandleMessage(MoveCommandFromMaster message)<br /> {<br /> MoveCompleted += this.TargetArrived;<br /> MoveToTarget(message.TargetPosition);<br /> }</p><p> // 到達目標<br /> public void TargetArrived()<br /> {<br /> MoveCompleted -= this.TargetArrived;<br /> timer.Interval = new TimeSpan(3);<br /> timer.Ticks += (sender, e) => IdleEnded();<br /> timer.Start();<br /> }<br /> // 發獃結束<br /> public void IdleEnded()<br /> {<br /> timer.Stop();<br /> Say("主人,我好餓...");<br /> }<br />}
藉助C#的event和lambda,已經使這些邏輯變得比大多數語言要簡單了。
如果引入Coroutine架構,則更為簡潔:
class Pet : Coroutine.Executor<br />{<br /> // 跟event類似,只是每次啟用後會卸載所有delegate<br /> public Coroutine.OneShotEvent MoveCompleted;<br /> public IEnumerable<Coroutine.Waiter> DoSomething()<br /> {<br /> // 等待訊息<br /> var extarctor = ExtractValue<MoveCommandFromMaster>();<br /> yield return WaitForMessage(extarctor);<br /> // 移動到目標地點<br /> MoveToTarget(extractor.Value.TargetPosition);<br /> yield return WaitForEvent(MoveCompleted);<br /> // 等待指定時間<br /> yield return WaitForSeconds(3.0f);<br /> Say("主人,我好餓");<br /> }<br />}
更進一步,可以組合多個事件等待以實現更複雜的邏輯:
{<br /> Send(new RequestMessage());<br /> var success = ExtractValue<SuccessMessage>();<br /> var failed = ExtractValue<FailedMessage>();<br /> yield return WaitForAny(<br /> WaitForMessage(success),<br /> WaitForMessage(failed),<br /> WaitForSeconds(30));<br /> if (success.Value != null)<br /> // 操作成功<br /> else if (failed.Value != null)<br /> // 操作失敗<br /> else<br /> // 操作逾時<br />}