c#、unity异步调⽤实际应⽤
使⽤TaskCompletionSource将委托回调形式的异步调⽤封装为await调⽤
以UGF的资源加载为例
调⽤这个接⼝
LoadAstCallbacks loadAstCallbacks = new LoadAstCallbacks(
(string astName, object ast, float duration, object urData) =>
{
},
(string astName, LoadResourceStatus status, string errorMessage, object urData) =>
{
});
resourceComponent.LoadAst(astName, loadAstCallbacks);
需要在LoadAstCallbacks的LoadAstSuccessCallback、LoadAstFailureCallback编写加载成功、失败的代码
封装为await调⽤
public static Task<object> LoadAstAsync2(this ResourceComponent resourceComponent, string astName)
{
TaskCompletionSource<object> loadAstTcs = new TaskCompletionSource<object>();
LoadAstCallbacks loadAstCallbacks = new LoadAstCallbacks((string _astName, object ast, float duration, object urData) =>
{
loadAstTcs.SetResult(ast);
}, (string _astName, LoadResourceStatus status, string errorMessage, object urData) =>
草原简笔画{
loadAstTcs.SetException(new Exception(errorMessage));
});
resourceComponent.LoadAst(astName, null, 0, loadAstCallbacks, null);
return loadAstTcs.Task;
}
object ast = await GameEntry.Resource.LoadAstAsync2("XXX/XXX.png");
使⽤TaskCompletionSource,只要分别在加载成功、加载失败调⽤SetResult、SetException即可
将继承YieldInstruction的类(AstBundleCreateRequest、WaitForEndOfFrame等)的协程调⽤封装为await调⽤
核⼼思路:
1、原本以协程⽅式调⽤,改为await调⽤
2、await调⽤的类需要实现GetAwaiter()⽅法、返回值继承INotifyCompletion接⼝,在协程调⽤完成后,调⽤awaiter.Complete触发异步结束,await返回值
3、封装⼀个MonoBehavior全局单例,执⾏StartCoroutine,调⽤YieldInstruction
核⼼代码:
public static SimpleCoroutineAwaiter<UnityEngine.Object> GetAwaiter(this AstBundleRequest instruction)
{
var awaiter = new SimpleCoroutineAwaiter<UnityEngine.Object>();
RunOnUnityScheduler(() => AsyncCoroutineRunner.Instance.StartCoroutine(
InstructionWrappers.AstBundleRequest(awaiter, instruction)));
return awaiter;
}
public static IEnumerator AstBundleRequest(
SimpleCoroutineAwaiter<UnityEngine.Object> awaiter, AstBundleRequest instruction)
{
yield return instruction;
awaiter.Complete(instruction.ast, null);
}
await astBundle.LoadAstAsync<GameObject>(astName)
代码分析:
SimpleCoroutineAwaiter继承INotifyCompletion接⼝,awaiter.Complete赋值并触发异步调⽤结束,AsyncCoroutineRunner是全局MonoBehavior单例
执⾏过程:
AsyncCoroutineRunner执⾏协程,AstBundleRequest执⾏完成后,awaiter.Complete赋值并触发异步调⽤结束,await接收到返回值继续往下执⾏
特别说明:
RunOnUnityScheduler:
Unity的⼀些API不能在⾮Unity的默认线程下调⽤,⽐如GameObject的SetActive,所以将其他线程Post到Unity默认线程执⾏
static void RunOnUnityScheduler(Action action)
{
if (SynchronizationContext.Current == SyncContextUtil.UnitySynchronizationContext)
{
action();
}
el
{
SyncContextUtil.UnitySynchronizationContext.Post(_ => action(), null);
}
}
举例:
public async void TaskRun() {
await Task.Run(() =>
{
m_QuitButton.SetActive(fal);
});
因人之力而敝之
}
修改后,就能正常执⾏:
public async void TaskRun() {
await Task.Run(() =>
{
SyncContextUtil.UnitySynchronizationContext.Post((obj)=> {
m_QuitButton.SetActive(fal);
}, null);
});
}
SyncContextUtil在场景加载前获取UnitySynchronizationContext
封装IEnumerator为await调⽤,实现委托回调形式的异步调⽤为await调⽤本质通过协程实现,Unity3dAsyncAwaitUtil封装IEnumerator为await调⽤
public static SimpleCoroutineAwaiter<T> GetAwaiter<T>(this IEnumerator<T> coroutine) {
var awaiter = new SimpleCoroutineAwaiter<T>();
RunOnUnityScheduler(() => AsyncCoroutineRunner.Instance.StartCoroutine(
new CoroutineWrapper<T>(coroutine, awaiter).Run()));
return awaiter;
}
public static SimpleCoroutineAwaiter<object> GetAwaiter(this IEnumerator coroutine) {
var awaiter = new SimpleCoroutineAwaiter<object>();
RunOnUnityScheduler(() => AsyncCoroutineRunner.Instance.StartCoroutine(
new CoroutineWrapper<object>(coroutine, awaiter).Run()));
return awaiter;
公司周年庆}
public static IEnumerator LoadAstAsync(this ResourceComponent resourceComponent, string astName)
{
AsyncLoadAstTag asyncLoadAstTag = AsyncLoadAstTag.Create();
LoadAstCallbacks loadAstCallbacks = new LoadAstCallbacks((string _astName, object ast, float duration, object urData) => {
asyncLoadAstTag.Ast = ast;
asyncLoadAstTag.LoadAstState = LoadAstState.Success;
}, (string _astName, LoadResourceStatus status, string errorMessage, object urData) =>
{
asyncLoadAstTag.Ast = null;
读书的谚语asyncLoadAstTag.LoadAstState = LoadAstState.Failure;
});
resourceComponent.LoadAst(astName, null, 0, loadAstCallbacks, null);
while (asyncLoadAstTag.LoadAstState == LoadAstState.Loading)
{
yield return null;
单反推荐}
yield return asyncLoadAstTag.Ast;
}
继承CustomYieldInstruction的类,封装为await调⽤
那个年代的我们
⾃⼰实现的YieldInstruction,可以通过keepWaiting作为异步调⽤结束的触发标志,实现如下
唯有牡丹public static AsyncLoadAstRequest LoadAstAsync3(this ResourceComponent resourceComponent, string astName)
{
AsyncLoadAstRequest asyncLoadAstRequest = AsyncLoadAstRequest.Create();
LoadAstCallbacks loadAstCallbacks = new LoadAstCallbacks((string _astName, object ast, float duration, object urData) => {
asyncLoadAstRequest.Ast = ast;
asyncLoadAstRequest.LoadAstState = LoadAstState.Success;
}, (string _astName, LoadResourceStatus status, string errorMessage, object urData) =>
{
asyncLoadAstRequest.Ast = null;
asyncLoadAstRequest.LoadAstState = LoadAstState.Failure;
});
resourceComponent.LoadAst(astName, null, 0, loadAstCallbacks, null);
return asyncLoadAstRequest;
}
public static SimpleCoroutineAwaiter<object> GetAwaiter(this AsyncLoadAstRequest instruction)
{
Debug.Log("GetAwaiter1");
var awaiter = new SimpleCoroutineAwaiter<object>();
RunOnUnityScheduler(() => AsyncCoroutineRunner.Instance.StartCoroutine(
AstRequest(awaiter, instruction)));
Debug.Log("GetAwaiter2");
return awaiter;
}
public static IEnumerator AstRequest(
SimpleCoroutineAwaiter<object> awaiter, AsyncLoadAstRequest instruction)
{
Debug.Log("AstRequest1");
yield return instruction;
Debug.Log("AstRequest2");
awaiter.Complete(instruction.Ast, null);
}
公益爱心
public class AsyncLoadAstRequest : CustomYieldInstruction
{
public object Ast;
public LoadAstState LoadAstState = LoadAstState.Init;
public override bool keepWaiting
{
get { return !(LoadAstState == LoadAstState.Failure || LoadAstState == LoadAstState.Success); }
}
public static AsyncLoadAstRequest Create()
{
AsyncLoadAstRequest asyncLoadAstRequest = new AsyncLoadAstRequest
{
LoadAstState = LoadAstState.Loading
};
return asyncLoadAstRequest;
}
}
在这⾥遇到了问题,如果代码是放在IEnumeratorAwaitExtensions.cs,是可以正确执⾏,放在其地⽅,结果不正确,甚⾄连GetAwaiter,AstRequest都不执⾏,