结合Thread Ninja明确与处理异步协程中的异常
Thread Ninja说明:
Thread Ninja - Multithread Coroutine
Requires Unity 3.4.0 or higher.
A simple script helps you write multithread coroutines.
Unity's coroutine is great, but it's not a real thread. And a background thread is not allowed to access Unity's API.
Thread Ninja combines coroutine & background thread, make a method both a coroutine and a background thread, makes your life easy with multithread programming.
Unity's coroutine is great, but it's not a real thread. And a background thread is not allowed to access Unity's API.
Thread Ninja combines coroutine & background thread, make a method both a coroutine and a background thread, makes your life easy with multithread programming.
对于yield语句不涉在try...catch中的情况:
1 // Use this for initialization 2 void Start () 3 { 4 GotoState(RunningBaseState.time); 5 this.StartCoroutineAsync(AsyncCoroutine()); 6 } 7 8 IEnumerator AsyncCoroutine() 9 { 10 while (true) 11 { 12 try 13 { 14 File.OpenRead("NotExist"); 15 } 16 catch (Exception e) 17 { 18 ZSLoger.Instance.Loger.Debug(e.Message); 19 } 20 Thread.Sleep(1000); 21 yield return ZS.JumpToUnity; 22 ZSLoger.Instance.Loger.Debug("Jump 2 unity in AsyncCoroutine."); 23 yield return ZS.JumpBack; 24 } 25 }
输出结果:
因为yield语句不能在try...catch中的情况修改Task.cs:
1 using UnityEngine; 2 using System.Collections; 3 using System.Threading; 4 5 namespace ZSThread 6 { 7 /// <summary> 8 /// Represents an async task. 9 /// </summary> 10 public class Task : IEnumerator 11 { 12 // implements IEnumerator to make it usable by StartCoroutine; 13 #region IEnumerator Interface 14 /// <summary> 15 /// The current iterator yield return value. 16 /// </summary> 17 public object Current { get; private set; } 18 19 /// <summary> 20 /// Runs next iteration. 21 /// </summary> 22 /// <returns><code>true</code> for continue, otherwise <code>false</code>.</returns> 23 public bool MoveNext() 24 { 25 return OnMoveNext(); 26 } 27 28 public void Reset() 29 { 30 // Reset method not supported by iterator; 31 throw new System.NotSupportedException( 32 "Not support calling Reset() on iterator."); 33 } 34 #endregion 35 36 // inner running state used by state machine; 37 public enum RunningState 38 { 39 Init, 40 RunningAsync, 41 PendingYield, 42 ToBackground, 43 RunningSync, 44 CancellationRequested, 45 Done, 46 Error 47 } 48 49 // routine user want to run; 50 protected readonly IEnumerator _innerRoutine; 51 52 // current running state; 53 private RunningState _state; 54 // last running state; 55 private RunningState _previousState; 56 // temporary stores current yield return value 57 // until we think Unity coroutine engine is OK to get it; 58 private object _pendingCurrent; 59 60 /// <summary> 61 /// Gets state of the task. 62 /// </summary> 63 public TaskState State 64 { 65 get 66 { 67 switch (_state) 68 { 69 case RunningState.CancellationRequested: 70 return TaskState.Cancelled; 71 case RunningState.Done: 72 return TaskState.Done; 73 case RunningState.Error: 74 return TaskState.Error; 75 case RunningState.Init: 76 return TaskState.Init; 77 default: 78 return TaskState.Running; 79 } 80 } 81 } 82 83 /// <summary> 84 /// Gets exception during running. 85 /// </summary> 86 public System.Exception Exception { get; protected set; } 87 88 public Task(IEnumerator routine) 89 { 90 _innerRoutine = routine; 91 // runs into background first; 92 _state = RunningState.Init; 93 } 94 95 /// <summary> 96 /// Cancel the task till next iteration; 97 /// </summary> 98 public void Cancel() 99 { 100 if (State == TaskState.Running) 101 { 102 GotoState(RunningState.CancellationRequested); 103 } 104 } 105 106 /// <summary> 107 /// A co-routine that waits the task. 108 /// </summary> 109 public IEnumerator Wait() 110 { 111 while (State == TaskState.Running) 112 yield return null; 113 } 114 115 // thread safely switch running state; 116 protected void GotoState(RunningState state) 117 { 118 if (_state == state) return; 119 120 lock (this) 121 { 122 // maintainance the previous state; 123 _previousState = _state; 124 _state = state; 125 } 126 } 127 128 // thread safely save yield returned value; 129 protected void SetPendingCurrentObject(object current) 130 { 131 lock (this) 132 { 133 _pendingCurrent = current; 134 } 135 } 136 137 // actual MoveNext method, controls running state; 138 protected bool OnMoveNext() 139 { 140 // no running for null; 141 if (_innerRoutine == null) 142 return false; 143 144 // set current to null so that Unity not get same yield value twice; 145 Current = null; 146 147 // loops until the inner routine yield something to Unity; 148 while (true) 149 { 150 // a simple state machine; 151 switch (_state) 152 { 153 // first, goto background; 154 case RunningState.Init: 155 GotoState(RunningState.ToBackground); 156 break; 157 // running in background, wait a frame; 158 case RunningState.RunningAsync: 159 return true; 160 161 // runs on main thread; 162 case RunningState.RunningSync: 163 MoveNextUnity(); 164 break; 165 166 // need switch to background; 167 case RunningState.ToBackground: 168 GotoState(RunningState.RunningAsync); 169 // call the thread launcher; 170 MoveNextAsync(); 171 return true; 172 173 // something was yield returned; 174 case RunningState.PendingYield: 175 if (_pendingCurrent == ZS.JumpBack) 176 { 177 // do not break the loop, switch to background; 178 GotoState(RunningState.ToBackground); 179 } 180 else if (_pendingCurrent == ZS.JumpToUnity) 181 { 182 // do not break the loop, switch to main thread; 183 GotoState(RunningState.RunningSync); 184 } 185 else 186 { 187 // not from the ZS, then Unity should get noticed, 188 // Set to Current property to achieve this; 189 Current = _pendingCurrent; 190 191 // yield from background thread, or main thread? 192 if (_previousState == RunningState.RunningAsync) 193 { 194 // if from background thread, 195 // go back into background in the next loop; 196 _pendingCurrent = ZS.JumpBack; 197 } 198 else 199 { 200 // otherwise go back to main thread the next loop; 201 _pendingCurrent = ZS.JumpToUnity; 202 } 203 204 // end this iteration and Unity get noticed; 205 return true; 206 } 207 break; 208 209 // done running, pass false to Unity; 210 case RunningState.Done: 211 case RunningState.CancellationRequested: 212 default: 213 return false; 214 } 215 } 216 } 217 218 // background thread launcher; 219 protected void MoveNextAsync() 220 { 221 ThreadPool.QueueUserWorkItem( 222 new WaitCallback(BackgroundRunner)); 223 } 224 225 // background thread function; 226 protected void BackgroundRunner(object state) 227 { 228 // just run the sync version on background thread; 229 MoveNextUnity(); 230 } 231 232 // run next iteration on main thread; 233 protected virtual void MoveNextUnity() 234 { 235 try 236 { 237 // run next part of the user routine; 238 var result = _innerRoutine.MoveNext(); 239 240 if (result) 241 { 242 // something has been yield returned, handle it; 243 SetPendingCurrentObject(_innerRoutine.Current); 244 GotoState(RunningState.PendingYield); 245 } 246 else 247 { 248 // user routine simple done; 249 GotoState(RunningState.Done); 250 } 251 } 252 catch (System.Exception ex) 253 { 254 // exception handling, save & log it; 255 this.Exception = ex; 256 Debug.LogError(string.Format("{0}\n{1}", ex.Message, ex.StackTrace)); 257 // then terminates the task; 258 GotoState(RunningState.Error); 259 } 260 } 261 } 262 }
使用情况:
1 using System.Collections; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Threading; 5 using UnityEngine; 6 using UnityEngine.Events; 7 using ZSThread; 8 9 public class RunLogicManager : SingletonLimit<RunLogicManager> 10 { 11 private RunningBaseState mState; 12 private Stack<RunningBaseState> mStateStack = new Stack<RunningBaseState>(); 13 14 public static RunLogicManager Instance 15 { 16 get 17 { 18 return (RunLogicManager)mInstance; 19 } 20 set 21 { 22 mInstance = value; 23 } 24 } 25 26 private void GotoState(RunningBaseState state) 27 { 28 if (mStateStack.Count > 0) 29 { 30 var tempState = mStateStack.Pop(); 31 tempState.Exit(); 32 } 33 mState = state; 34 mStateStack.Push(mState); 35 mState.Enter(); 36 } 37 38 // Use this for initialization 39 void Start () 40 { 41 GotoState(RunningBaseState.time); 42 43 ExceptionTask t = new ExceptionTask(AsyncCoroutine(), mono => 44 { 45 ZSLoger.Instance.Loger.Debug("处理异常,此调用处于多线程."); 46 }, this); 47 StartCoroutine(t); 48 } 49 50 IEnumerator AsyncCoroutine() 51 { 52 while (true) 53 { 54 File.OpenRead("NotExist"); 55 Thread.Sleep(1000); 56 yield return ZS.JumpToUnity; 57 ZSLoger.Instance.Loger.Debug("Jump 2 unity in AsyncCoroutine."); 58 yield return ZS.JumpBack; 59 } 60 } 61 62 // Update is called once per frame 63 void Update () 64 { 65 66 } 67 } 68 69 public class ExceptionTask : Task 70 { 71 private MonoBehaviour mono; 72 private UnityAction<MonoBehaviour> exceptionHandle; 73 public ExceptionTask(IEnumerator routine, UnityAction<MonoBehaviour> exceptionHandle, MonoBehaviour mono) : base(routine) 74 { 75 this.mono = mono; 76 this.exceptionHandle = exceptionHandle; 77 } 78 79 // run next iteration on main thread; 80 protected override void MoveNextUnity() 81 { 82 try 83 { 84 // run next part of the user routine; 85 var result = _innerRoutine.MoveNext(); 86 87 if (result) 88 { 89 // something has been yield returned, handle it; 90 SetPendingCurrentObject(_innerRoutine.Current); 91 GotoState(RunningState.PendingYield); 92 } 93 else 94 { 95 // user routine simple done; 96 GotoState(RunningState.Done); 97 } 98 } 99 catch (System.Exception ex) 100 { 101 this.Exception = ex; 102 //Debug.LogError(string.Format("{0}\n{1}", ex.Message, ex.StackTrace)); 103 ZSLoger.Instance.Loger.Debug("handle exception."); 104 if (exceptionHandle != null) 105 { 106 exceptionHandle.Invoke(mono); 107 } 108 // then terminates the task; 109 GotoState(RunningState.Error); 110 } 111 } 112 }
输出结果:
关于unity协程深度的扩展参考博文:http://blog.csdn.net/ybhjx/article/details/55188777