基于Unity的Hololens应用如何后台加载字节数组且不阻塞UI交互
问题根因
- 直接在
UnityEngine.WSA.Application.InvokeOnUIThread回调中同步执行LoadBytes逻辑时,所有IO操作都跑在UI线程,必然阻塞UI导致卡顿 - 此前
Task.Run触发UI冻结的核心原因有两点:- UWP的
StorageFile属于COM对象,仅允许在创建它的UI线程访问成员(包括Path属性),跨线程访问会触发线程同步甚至异常,间接阻塞UI - 非线程安全的
List<T>直接跨线程读写,会触发线程竞争,严重时会直接闪退
- UWP的
解决方案
核心逻辑为先在UI线程提取所有待加载文件的路径字符串,再用异步IO在后台加载字节数组,加载完成后切回UI线程更新结果,全程不阻塞UI交互。
代码修改示例
首先修改按钮触发的加载函数:
public void LoadSomething() { UnityEngine.WSA.Application.InvokeOnUIThread(async () => { // 加载首批20个字节数组并展示第一张图片 var firstBatch = manager.listOfStorageFiles.Take(20).ToList(); foreach (var file in firstBatch) { var data = File.ReadAllBytes(file.Path); manager.byteList.Add(data); } // 此处补充展示第一张Sprite的业务逻辑 ShowFirstSprite(); // UI线程提前提取所有剩余文件的路径,避免跨线程访问StorageFile var remainingPaths = manager.listOfStorageFiles.Skip(20) .Select(f => f.Path) .ToList(); // 后台异步加载剩余字节,不阻塞UI _ = LoadRemainingBytesAsync(remainingPaths); }, false); }
新增异步加载剩余文件的函数:
private async Task LoadRemainingBytesAsync(List<string> remainingPaths) { await Task.Run(async () => { foreach (var path in remainingPaths) { // 采用异步IO读取文件,进一步降低线程阻塞 byte[] fileData = await File.ReadAllBytesAsync(path); // 切回UI线程更新字节列表,避免线程安全问题 UnityEngine.WSA.Application.InvokeOnUIThread(() => { manager.byteList.Add(fileData); }, false); // 可选:每加载完成加10ms等待,避免CPU占用过高导致UI掉帧 await Task.Delay(10); } }); // 全部加载完成后的回调逻辑(可选) UnityEngine.WSA.Application.InvokeOnUIThread(() => { Debug.Log("所有图片字节数组预加载完成"); }, false); }
注意事项
- 若需要更高加载速度,可删除
Task.Delay(10)逻辑,Hololens设备CPU最多支持同时并行加载2-3个文件,可自行调整并行度,注意不要超过3个避免IO阻塞。 - 所有Unity对象的读写操作必须在UI线程执行,字节数组属于普通C#对象,可在后台线程创建,仅更新到MonoBehaviour字段时切回UI线程即可。
内容的提问来源于stack exchange,提问作者AhneR94
相关产品推荐
相关产品推荐

