unity的异步操作
1.C#提供了两个关键字来实现异步async和await
async
关键字用于声明一个异步方法。该方法内部可以使用 await
关键字来等待异步操作的完成。当方法被标记为 async
时,它隐式地表示该方法可能会包含一个或多个 await
表达式。重要的是要注意,async
方法通常会返回一个 Task
或 Task<T>
类型的对象,其中 T
是方法的返回类型。
await
关键字用于等待一个异步操作的完成。它只能在被 async
修饰的方法内部使用。当遇到 await
表达式时,编译器会自动将方法分成两部分:await
表达式之前的代码会在原始线程上同步执行,而 await
表达式之后的代码会在异步操作完成时,在另一个线程(或从线程池中获取的线程)上继续执行。这个过程是自动的,不需要开发者手动管理线程。
例子:
using System; using System.IO; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { string content = await ReadFileAsync("example.txt"); Console.WriteLine(content); } static async Task<string> ReadFileAsync(string filePath) { using (StreamReader reader = new StreamReader(filePath)) { // 注意:StreamReader 没有直接的异步读取方法,这里为了示例 // 我们假设有一个,但在现实中,你可能会使用 File.ReadAllTextAsync 等 // 这里使用 Task.Run 来模拟异步操作 string content = await Task.Run(() => { return reader.ReadToEnd(); }); return content; } } } // 注意:上面的 ReadFileAsync 方法中使用了 Task.Run 来模拟异步操作, // 但在实际应用中,读取文件应该使用 File.ReadAllTextAsync 或类似的异步API。
2.UniTask
Task的升级版本(第三方弄出来代替Task和协程的方案),0GC的真异步解决方案。
例子:
public async UniTask AsyncSetImage(string address) { ZCancelSource.Return(ref cancelSource_); cancelSource_ = ZCancelSource.Rent(); try { Sprite spr = await ZResLoader.Instance.LoadAssetAsync<Sprite>(address, cancelSource_.CreateToken(), 1000); this.sprite = spr; ZCancelSource.Return(ref cancelSource_); } }
3.打断操作
unitask提供了CancelTokenSource,但会有GC,所以最好还是自己写一套
ZCancelSource:管理一组CancelToken(uint类型,每次照上一次递增,在0-uint.maxvalue中循环,维护一个dict来标记token是否存在),每个CancelToken对应一个异步逻辑,可以指定打断/取消某一个异步逻辑。
private static async UniTask<object> loadAssetAsyncInternal(uint realAddress, uint token) { ... if (ZCancelSource.IsCanceled(token)) { throw new ArgumentException($"[ZResLoader] invaid token on LoadAssetAsync, token={token}"); } ... while (!asset.IsDone && !ZCancelSource.IsCanceled(token)) { await UniTask.Yield(); } if (ZCancelSource.IsCanceled(token)) { asset.Release(); throw ZCancelSource.CancelException; } ZCancelSource.ReleaseToken(token); ... }