结合Thread Ninja明确与处理异步协程中的异常

时间:2022-12-03 22:42:15

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中的情况:
     // Use this for initialization
void Start ()
{
GotoState(RunningBaseState.time);
this.StartCoroutineAsync(AsyncCoroutine());
} IEnumerator AsyncCoroutine()
{
while (true)
{
try
{
File.OpenRead("NotExist");
}
catch (Exception e)
{
ZSLoger.Instance.Loger.Debug(e.Message);
}
Thread.Sleep();
yield return ZS.JumpToUnity;
ZSLoger.Instance.Loger.Debug("Jump 2 unity in AsyncCoroutine.");
yield return ZS.JumpBack;
}
}

输出结果:

结合Thread Ninja明确与处理异步协程中的异常

 
因为yield语句不能在try...catch中的情况修改Task.cs:
 using UnityEngine;
using System.Collections;
using System.Threading; namespace ZSThread
{
/// <summary>
/// Represents an async task.
/// </summary>
public class Task : IEnumerator
{
// implements IEnumerator to make it usable by StartCoroutine;
#region IEnumerator Interface
/// <summary>
/// The current iterator yield return value.
/// </summary>
public object Current { get; private set; } /// <summary>
/// Runs next iteration.
/// </summary>
/// <returns><code>true</code> for continue, otherwise <code>false</code>.</returns>
public bool MoveNext()
{
return OnMoveNext();
} public void Reset()
{
// Reset method not supported by iterator;
throw new System.NotSupportedException(
"Not support calling Reset() on iterator.");
}
#endregion // inner running state used by state machine;
public enum RunningState
{
Init,
RunningAsync,
PendingYield,
ToBackground,
RunningSync,
CancellationRequested,
Done,
Error
} // routine user want to run;
protected readonly IEnumerator _innerRoutine; // current running state;
private RunningState _state;
// last running state;
private RunningState _previousState;
// temporary stores current yield return value
// until we think Unity coroutine engine is OK to get it;
private object _pendingCurrent; /// <summary>
/// Gets state of the task.
/// </summary>
public TaskState State
{
get
{
switch (_state)
{
case RunningState.CancellationRequested:
return TaskState.Cancelled;
case RunningState.Done:
return TaskState.Done;
case RunningState.Error:
return TaskState.Error;
case RunningState.Init:
return TaskState.Init;
default:
return TaskState.Running;
}
}
} /// <summary>
/// Gets exception during running.
/// </summary>
public System.Exception Exception { get; protected set; } public Task(IEnumerator routine)
{
_innerRoutine = routine;
// runs into background first;
_state = RunningState.Init;
} /// <summary>
/// Cancel the task till next iteration;
/// </summary>
public void Cancel()
{
if (State == TaskState.Running)
{
GotoState(RunningState.CancellationRequested);
}
} /// <summary>
/// A co-routine that waits the task.
/// </summary>
public IEnumerator Wait()
{
while (State == TaskState.Running)
yield return null;
} // thread safely switch running state;
protected void GotoState(RunningState state)
{
if (_state == state) return; lock (this)
{
// maintainance the previous state;
_previousState = _state;
_state = state;
}
} // thread safely save yield returned value;
protected void SetPendingCurrentObject(object current)
{
lock (this)
{
_pendingCurrent = current;
}
} // actual MoveNext method, controls running state;
protected bool OnMoveNext()
{
// no running for null;
if (_innerRoutine == null)
return false; // set current to null so that Unity not get same yield value twice;
Current = null; // loops until the inner routine yield something to Unity;
while (true)
{
// a simple state machine;
switch (_state)
{
// first, goto background;
case RunningState.Init:
GotoState(RunningState.ToBackground);
break;
// running in background, wait a frame;
case RunningState.RunningAsync:
return true; // runs on main thread;
case RunningState.RunningSync:
MoveNextUnity();
break; // need switch to background;
case RunningState.ToBackground:
GotoState(RunningState.RunningAsync);
// call the thread launcher;
MoveNextAsync();
return true; // something was yield returned;
case RunningState.PendingYield:
if (_pendingCurrent == ZS.JumpBack)
{
// do not break the loop, switch to background;
GotoState(RunningState.ToBackground);
}
else if (_pendingCurrent == ZS.JumpToUnity)
{
// do not break the loop, switch to main thread;
GotoState(RunningState.RunningSync);
}
else
{
// not from the ZS, then Unity should get noticed,
// Set to Current property to achieve this;
Current = _pendingCurrent; // yield from background thread, or main thread?
if (_previousState == RunningState.RunningAsync)
{
// if from background thread,
// go back into background in the next loop;
_pendingCurrent = ZS.JumpBack;
}
else
{
// otherwise go back to main thread the next loop;
_pendingCurrent = ZS.JumpToUnity;
} // end this iteration and Unity get noticed;
return true;
}
break; // done running, pass false to Unity;
case RunningState.Done:
case RunningState.CancellationRequested:
default:
return false;
}
}
} // background thread launcher;
protected void MoveNextAsync()
{
ThreadPool.QueueUserWorkItem(
new WaitCallback(BackgroundRunner));
} // background thread function;
protected void BackgroundRunner(object state)
{
// just run the sync version on background thread;
MoveNextUnity();
} // run next iteration on main thread;
protected virtual void MoveNextUnity()
{
try
{
// run next part of the user routine;
var result = _innerRoutine.MoveNext(); if (result)
{
// something has been yield returned, handle it;
SetPendingCurrentObject(_innerRoutine.Current);
GotoState(RunningState.PendingYield);
}
else
{
// user routine simple done;
GotoState(RunningState.Done);
}
}
catch (System.Exception ex)
{
// exception handling, save & log it;
this.Exception = ex;
Debug.LogError(string.Format("{0}\n{1}", ex.Message, ex.StackTrace));
// then terminates the task;
GotoState(RunningState.Error);
}
}
}
}

使用情况:

 using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using UnityEngine;
using UnityEngine.Events;
using ZSThread; public class RunLogicManager : SingletonLimit<RunLogicManager>
{
private RunningBaseState mState;
private Stack<RunningBaseState> mStateStack = new Stack<RunningBaseState>(); public static RunLogicManager Instance
{
get
{
return (RunLogicManager)mInstance;
}
set
{
mInstance = value;
}
} private void GotoState(RunningBaseState state)
{
if (mStateStack.Count > )
{
var tempState = mStateStack.Pop();
tempState.Exit();
}
mState = state;
mStateStack.Push(mState);
mState.Enter();
} // Use this for initialization
void Start ()
{
GotoState(RunningBaseState.time); ExceptionTask t = new ExceptionTask(AsyncCoroutine(), mono =>
{
ZSLoger.Instance.Loger.Debug("处理异常,此调用处于多线程.");
}, this);
StartCoroutine(t);
} IEnumerator AsyncCoroutine()
{
while (true)
{
File.OpenRead("NotExist");
Thread.Sleep();
yield return ZS.JumpToUnity;
ZSLoger.Instance.Loger.Debug("Jump 2 unity in AsyncCoroutine.");
yield return ZS.JumpBack;
}
} // Update is called once per frame
void Update ()
{ }
} public class ExceptionTask : Task
{
private MonoBehaviour mono;
private UnityAction<MonoBehaviour> exceptionHandle;
public ExceptionTask(IEnumerator routine, UnityAction<MonoBehaviour> exceptionHandle, MonoBehaviour mono) : base(routine)
{
this.mono = mono;
this.exceptionHandle = exceptionHandle;
} // run next iteration on main thread;
protected override void MoveNextUnity()
{
try
{
// run next part of the user routine;
var result = _innerRoutine.MoveNext(); if (result)
{
// something has been yield returned, handle it;
SetPendingCurrentObject(_innerRoutine.Current);
GotoState(RunningState.PendingYield);
}
else
{
// user routine simple done;
GotoState(RunningState.Done);
}
}
catch (System.Exception ex)
{
this.Exception = ex;
//Debug.LogError(string.Format("{0}\n{1}", ex.Message, ex.StackTrace));
ZSLoger.Instance.Loger.Debug("handle exception.");
if (exceptionHandle != null)
{
exceptionHandle.Invoke(mono);
}
// then terminates the task;
GotoState(RunningState.Error);
}
}
}

输出结果:

结合Thread Ninja明确与处理异步协程中的异常

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