Unity中DownloadScreenshotFromStorageAsync函数线程阻塞及TaskCanceledException问题排查
解决Unity Firebase存储下载异步函数阻塞/死锁问题
看起来你遇到了Unity中Firebase Storage异步下载的典型死锁和异常处理问题,我来帮你拆解并修复这个函数:
核心问题分析
- 死锁根源:Unity的
await默认会捕获主线程同步上下文,当Firebase后台任务完成后试图切回主线程时,如果主线程被其他同步操作占用(比如UI阻塞、同步锁等),就会触发死锁。 - TaskCanceledException:大概率是因为下载任务超时或被意外取消,但你的函数没有处理这类异常,也没有设置合理的超时机制。
- 失败时未返回null:当前代码缺少全局异常捕获,无法保证所有失败场景都返回null。
修复后的完整代码
public async Task<Texture2D> DownloadScreenshotFromStorageAsync(string url) { try { Debug.Log("Downloading from Storage...\n" + url); // 获取Firebase存储引用(补全你的初始化代码) var cloud = _firebaseStorage.GetReferenceFromUrl(url); // 限制下载的图片大小为5MB,可根据需求调整 var dlScreenshot_task = cloud.GetBytesAsync(1024 * 1024 * 5); // 用Task.WhenAny实现超时机制,避免无限阻塞(这里设为10秒) var completedTask = await Task.WhenAny(dlScreenshot_task, Task.Delay(TimeSpan.FromSeconds(10))); if (completedTask != dlScreenshot_task) { Debug.LogWarning("Download timed out for URL: " + url); return null; } // 检查下载任务是否成功完成 if (!dlScreenshot_task.IsCompletedSuccessfully) { Debug.LogError("Download failed with error: " + dlScreenshot_task.Exception?.Message); return null; } byte[] screenshotBytes = dlScreenshot_task.Result; // 纹理操作需在主线程执行,这里直接创建加载即可 Texture2D texture = new Texture2D(2, 2); if (texture.LoadImage(screenshotBytes)) { return texture; } else { Debug.LogError("Failed to load image bytes into Texture2D"); UnityEngine.Object.Destroy(texture); return null; } } catch (Exception ex) { // 捕获所有异常(包括TaskCanceledException、网络错误等) Debug.LogError("Unexpected error during download: " + ex.Message); return null; } }
关键修复点说明
- 超时机制:通过
Task.WhenAny同时等待下载任务和延迟任务,防止任务无限阻塞,超时直接返回null。 - 全局异常捕获:整个逻辑包裹在
try-catch中,确保任何异常场景都能返回null,符合你的需求。 - 纹理加载安全:创建Texture后如果加载失败,及时销毁对象避免内存泄漏。
- 避免死锁:通过
Task.WhenAny绕开强制切回主线程的逻辑,同时确保纹理操作在Unity允许的主线程执行。
额外注意事项
- 确保
_firebaseStorage已经在Start或Awake中完成初始化,避免空引用错误。 - 如果你的图片尺寸较大,调整
GetBytesAsync的大小限制参数。 - 若需要在后台线程处理字节数据,可单独拆分逻辑,但纹理创建必须回到主线程。
内容的提问来源于stack exchange,提问作者Jimmy Alger
相关产品推荐
相关产品推荐

