2012年3月14日 星期三

我說Coroutine,不是這樣用的~~




昨天在測試的時候發生了很嚴重的當機事件,不管用甚麼廠牌的Android的手機,在遊玩過程中會不定時的無任何警示訊息自動跳出,從LogCat看訊息發現:
  1. ERROR/InputDispatcher(284): channel '418b6698 path.to.our.app (server)' ~ Consumer closed input channel or an error occurred.  events=0x8
  2. ERROR/InputDispatcher(284): channel '418b6698 path.to.our.app (server)' ~ Channel is unrecoverably broken and will be disposed!

這時先冷近想想這幾天加了甚麼東西?靈機一動,會不會是State Machine用Coroutine執行出了問題呢?來看看State在實做上的寫法:

internal abstract class XAIState {
 protected XAIController _m_ai = null;
 public XAIController ai { get { return _m_ai; } }
 public virtual void Enter ( XAIController ai ) {}
 public virtual IEnumerator Execute () { yield return null; }
 public virtual void Exit () {}
}

internal class XAIWaitState : XAIState { 
 public override void Enter ( XAIController ai ) {
  _m_ai = ai;
  ai.StartCoroutine( Execute() );
 }
 
 public IEnumerator void Execute () {
            while( ture ) {
                //Do somthing... for Waiting!!
                yield return null;
            }
 }
 
 public override void Exit () {
                ai.StopAllCoroutine( );
 }
}

internal class XAIRunState : XAIState { 
 public override void Enter ( XAIController ai ) {
  _m_ai = ai;
  ai.StartCoroutine( Execute() );
 }
 
 public IEnumerator void Execute () {
            while( ture ) {
                //Do somthing... for Run
                yield return null;
            }
 }
 
 public override void Exit () {
                ai.StopAllCoroutine( );
 }
}

public class XAIController : MonoBehaviour {
 private XAIState _m_currentState = null;
 internal XAIWaitState _m_waitState = new XAIWaitState();
 internal XAIRunState _m_runState = new XAIRunState();

        protected void Start () {
           SwitchState( _m_waitState );
        }

        private void SwitchState ( XAIState state ) {
           if( _m_currentState != null )
               _m_currentState.Exit();

           _m_currentState  = state;

           if( _m_currentState != null )
               _m_currentState.Enter();
        }
}
從上面看來原碼在Compile上沒錯、也沒有WARNING,不會有問題吧!殊不知,State在執行期間頻繁的切換Start、Stop Coroutine的動作會讓Andorid在不定時的狀態下Crash,而且完全沒有任何警訊!!

結論:
如果你也有遇到相同的問題,請想想有沒有甚麼地方跟我一樣,在Class之間彼此不斷的Start、Stop Coroutine,如果有!那可能就是Crash的關鍵。

至於我怎麼解掉這個問題的呢?方法很爛,我想大家因該都知道了,就是用void Update()來解決,不要搖屁股的自以為用Coroutine很屌 ... :P



作者:Bric Lin, Email: ericlin09@gmail.com
曾任職台灣某遊戲公司研發Game Engine,為書籍“OGRE 入門指南”的譯者之一,專攻Rendering技術與遊戲開發,目前為獨立開發團隊CocosPlay主程式


5 則留言:

  1. 請問一下
    在程式中使用多執行緒功能
    並在執行緒動作使用無窮迴圈
    對效能的影響為何??
    一般在主程式中加入一個無窮迴圈
    就會吃掉幾十%的CPU使用量
    那如果是用多執行序呢?
    也會這麼慘嗎?

    回覆刪除
    回覆
    1. 這問題要看你CPU是幾核心而定,不過你說會吃掉幾十%有這麼多嗎?一般MMOG的Client也會開一個Thread來聽封包,Client的Rendering thread也都可以算是一個無窮迴圈在Run,應該部會到都不做任何事情就吃掉這麼多,也許你該檢查看看內部有哪個部分是最耗時間的。

      刪除
    2. 謝謝回答
      說實在我不太懂電腦效能跟指令到底是怎麼運作的
      int i = 1;
      while(true)
      i++;
      這樣的程式
      是不是電腦會盡可能用最快的速度去對i作+1的動作
      還是說 有個固定的頻率回來執行程式碼下的指令?

      刪除
    3. 不用想太多,就是這樣寫。編譯器會為你準備好一切滴接最佳化的事情。

      刪除
    4. 感恩
      看來我的底層觀念有問題...

      刪除