Task.WhenAll任务取消异常:Unity单元测试首测失败后续通过
问题排查:Unity异步初始化取消逻辑的测试异常
代码背景
InitializerProcess.Init 核心逻辑
// This is part of the InitializerProcess.Init method try { InitializationResult[] rs = await Task.Run(() => Task.WhenAll(tasks), m_source.Token); } catch (OperationCanceledException e) { Debug.Log($"{nameof(OperationCanceledException)} thrown with message: {e.Message}"); return null; } finally { m_source?.Dispose(); }
Unity单元测试用例
[UnityTest] public IEnumerator InitializerTestCancelPasses() { var init = new InitializerProces(); Task<List<InitializationResult>> asyncOperation = init.Init(m_initializer); while (!asyncOperation.IsCompleted) { init.Cancel(); yield return null; } Assert.IsNull(asyncOperation.Result); }
异常现象
测试任务为两个等待0.5秒的初始化任务,运行测试时出现异常:首次运行测试不通过,后续重复运行则通过。怀疑后续运行实际取消了前一次测试的遗留任务,并非当前测试的取消逻辑正常生效。
排查方向
- 令牌与任务绑定逻辑问题:当前代码将取消令牌传入
Task.Run而非Task.WhenAll,仅能取消Task.Run的调度逻辑,无法直接取消内部的tasks集合。需要确保每个子任务都监听取消令牌,或把令牌传入Task.WhenAll的执行上下文。 - Unity单元测试协程与异步兼容问题:截至2024年10月,Unity正式版测试框架对异步/等待的支持不完善,协程等待异步任务完成的时机可能存在偏差。可尝试在测试中调整取消时机(比如延迟固定帧后再调用
Cancel,而非进入循环立即调用),或改用更稳妥的异步等待方式。 - 测试实例隔离问题:
InitializerProcess的实例可能存在静态引用或资源未完全释放的情况,导致前一次测试的CancellationTokenSource影响后续测试。需检查Cancel方法是否正确标记m_source已取消,且Dispose是否彻底释放资源。 - 任务执行时机问题:首次运行时,
Cancel调用可能早于tasks开始执行,导致取消信号未被子任务捕获;后续运行时因线程池预热等环境变化,任务启动延迟,取消信号生效。可在测试中添加短暂延迟(比如yield return new WaitForSeconds(0.1f))后再调用Cancel验证。
内容的提问来源于stack exchange,提问作者Everts
相关产品推荐
相关产品推荐

