结合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.
 
对于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 }
View Code

使用情况:

  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 }
View Code

输出结果:

关于unity协程深度的扩展参考博文:http://blog.csdn.net/ybhjx/article/details/55188777

posted @ 2017-11-03 22:19  非法关键字  阅读(723)  评论(0编辑  收藏  举报