Unity 简单高效的异步库UniTask
UniTask
为Unity提供一个高性能,零GC的async/await异步方案。
- 基于值类型的
UniTask<T>
和自定义的 AsyncMethodBuilder 来实现0GC - 使所有 Unity 的 AsyncOperations 和 Coroutines 可等待
- 基于 PlayerLoop 的任务(
UniTask.Yield
,UniTask.Delay
,UniTask.DelayFrame
, etc..) 可以替换所有协程操作 - 对MonoBehaviour 消息事件和 uGUI 事件进行 可等待/异步枚举 拓展
- 完全在 Unity 的 PlayerLoop 上运行,因此不使用Thread,并且同样能在 WebGL、wasm 等平台上运行。
- 带有 Channel 和 AsyncReactiveProperty的异步 LINQ,
- 提供一个 TaskTracker EditorWindow 以追踪所有UniTask分配来预防内存泄漏
- 与原生 Task/ValueTask/IValueTaskSource 高度兼容的行为
// 使用UniTask所需的命名空间 using Cysharp.Threading.Tasks; // 你可以返回一个形如 UniTask<T>(或 UniTask) 的类型,这种类型事为Unity定制的,作为替代原生Task<T>的轻量级方案 // 为Unity集成的 0GC,快速调用,0消耗的 async/await 方案 async UniTask<string> DemoAsync() { // 你可以等待一个Unity异步对象 var asset = await Resources.LoadAsync<TextAsset>("foo"); var txt = (await UnityWebRequest.Get("https://...").SendWebRequest()).downloadHandler.text; await SceneManager.LoadSceneAsync("scene2"); // .WithCancellation 会启用取消功能,GetCancellationTokenOnDestroy 表示获取一个依赖对象生命周期的Cancel句柄,当对象被销毁时,将会调用这个Cancel句柄,从而实现取消的功能 var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy()); // .ToUniTask 可接收一个 progress 回调以及一些配置参数,Progress.Create是IProgress<T>的轻量级替代方案 var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x))); // 等待一个基于帧的延时操作(就像一个协程一样) await UniTask.DelayFrame(100); // yield return new WaitForSeconds/WaitForSecondsRealtime 的替代方案 await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false); // 可以等待任何 playerloop 的生命周期(PreUpdate, Update, LateUpdate, 等...) await UniTask.Yield(PlayerLoopTiming.PreLateUpdate); // yield return null 替代方案 await UniTask.Yield(); await UniTask.NextFrame(); // WaitForEndOfFrame 替代方案 (需要 MonoBehaviour(CoroutineRunner)) await UniTask.WaitForEndOfFrame(this); // this 是一个 MonoBehaviour // yield return new WaitForFixedUpdate 替代方案,(和 UniTask.Yield(PlayerLoopTiming.FixedUpdate) 效果一样) await UniTask.WaitForFixedUpdate(); // yield return WaitUntil 替代方案 await UniTask.WaitUntil(() => isActive == false); // WaitUntil拓展,指定某个值改变时触发 await UniTask.WaitUntilValueChanged(this, x => x.isActive); // 你可以直接 await 一个 IEnumerator 协程 await FooCoroutineEnumerator(); // 你可以直接 await 一个原生 task await Task.Run(() => 100); // 多线程示例,在此行代码后的内容都运行在一个线程池上 await UniTask.SwitchToThreadPool(); /* 工作在线程池上的代码 */ // 转回主线程 await UniTask.SwitchToMainThread(); // 获取异步的 webrequest async UniTask<string> GetTextAsync(UnityWebRequest req) { var op = await req.SendWebRequest(); return op.downloadHandler.text; } var task1 = GetTextAsync(UnityWebRequest.Get("http://google.com")); var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.com")); var task3 = GetTextAsync(UnityWebRequest.Get("http://yahoo.com")); // 构造一个async-wait,并通过元组语义轻松获取所有结果 var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3); // WhenAll简写形式 var (google2, bing2, yahoo2) = await (task1, task2, task3); // 返回一个异步值,或者你也可以使用`UniTask`(无结果), `UniTaskVoid`(协程,不可等待) return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found"); }
中文手册:UniTask/README_CN.md at master · Cysharp/UniTask (github.com)
源代码:Cysharp/UniTask: Provides an efficient allocation free async/await integration for Unity. (github.com)
API:Cysharp.Threading.Tasks Namespace | UniTask
做个简单的测试
using System.Collections; using System.Collections.Generic; using UnityEngine; using Cysharp.Threading.Tasks; using UnityEngine.Networking; public class UniTaskTest : MonoBehaviour { void Start() { Test().Forget(); } async UniTaskVoid Test() { Debug.LogError(System.Threading.Thread.CurrentThread.ManagedThreadId);//主线程 await UniTask.WaitUntil(() => { Debug.LogError(System.Threading.Thread.CurrentThread.ManagedThreadId); return true; });//主线程 Debug.LogError(System.Threading.Thread.CurrentThread.ManagedThreadId);//主线程 await UniTask.RunOnThreadPool(()=> Debug.LogError(System.Threading.Thread.CurrentThread.ManagedThreadId));//子线程 Debug.LogError(System.Threading.Thread.CurrentThread.ManagedThreadId);//主线程 await new UnityWebRequest("www.baidu.com", "GET", new Handler(), null).SendWebRequest();//主线程 await UniTask.SwitchToTaskPool(); //await new UnityWebRequest("www.baidu.com", "GET", new Handler(), null).SendWebRequest();//报错:Create can only be called from the main thread. Debug.LogError(System.Threading.Thread.CurrentThread.ManagedThreadId);//子线程 await UniTask.SwitchToMainThread(); Debug.LogError(System.Threading.Thread.CurrentThread.ManagedThreadId);//主线程 } public class Handler : DownloadHandlerScript { public Handler() { Debug.LogError(System.Threading.Thread.CurrentThread.ManagedThreadId); } } }
博客园Jason_c微信打赏码
如果本篇文档对你有帮助,打赏Jason_c根华子吧,他的私房钱被老婆没收了,呜呜!
