Unity UWP第三次API调用触发TaskCancellationException问题求助
Unity UWP 第三次API调用固定抛出TaskCanceledException 排查方案
问题现象
- 运行环境:Unity UWP平台
- 异常表现:顺序调用3个接口时,无论如何调整调用顺序,固定第三次调用抛出
TaskCanceledException;仅调用1个或2个接口时逻辑运行正常,后端API服务本身无故障。 - 原实现代码:
public class DashboardManager : MonoBehaviour { public TMP_Text Status; async Task Awake() { await GetPerformanceInfo().ConfigureAwait(false); await GetProductionInfo().ConfigureAwait(false); await GetChecklistInfo().ConfigureAwait(false); } private async Task GetChecklistInfo() { try { HttpClient httpClient = new HttpClient((HttpMessageHandler)ApiHelpers.HttpClientHandler); httpClient.BaseAddress = new Uri(ApiHelpers.PlantApiBaseAddress.CheckBaseUrl(true)); var plantApiClient = new PlantApiClient(httpClient, default); var checklists = await plantApiClient.GetChecklistWidgetInfo(UserSettings.Location); } catch (LogoutException ex) { SceneManager.LoadScene("LoginScene"); } catch (TaskCanceledException ex) { Status.text = "Checklist: " + ex.Message; } catch (Exception ex) { Status.text = "Checklist: " + ex.Message; } } public void OnClickButton(string guid) { GlobalManager.CurrentChecklistID = guid; SceneManager.LoadScene("ChecklistsScene"); } private async Task GetPerformanceInfo() { try { HttpClient httpClient = new HttpClient((HttpMessageHandler)ApiHelpers.HttpClientHandler); httpClient.BaseAddress = new Uri(ApiHelpers.PlantApiBaseAddress.CheckBaseUrl(true)); var plantApiClient = new PlantApiClient(httpClient, default); var perfInfo = await plantApiClient.GetPerformanceWidgetInfo(UserSettings.Location); } catch (LogoutException ex) { SceneManager.LoadScene("LoginScene"); } catch (TaskCanceledException ex) { Status.text = "Performance: " + ex.Message; } catch (Exception ex) { Status.text = "Performance: " + ex.Message; } } private async Task GetProductionInfo() { try { HttpClient httpClient = new HttpClient((HttpMessageHandler)ApiHelpers.HttpClientHandler); httpClient.BaseAddress = new Uri(ApiHelpers.PlantApiBaseAddress.CheckBaseUrl(true)); var plantApiClient = new PlantApiClient(httpClient, default); var prodInfo = await plantApiClient.GetProductionWidgetInfo(UserSettings.Location); } catch (LogoutException ex) { SceneManager.LoadScene("LoginScene"); } catch (TaskCanceledException ex) { Status.text = "Production: " + ex.Message; } catch (Exception ex) { Status.text = "Production: " + ex.Message; } } }
根因定位
这个异常是两个UWP/Unity异步机制的限制共同触发的:
- UWP HttpClient默认连接数限制:UWP平台原生
HttpClientHandler默认对同域名服务的最大并发连接数为2,代码每次调用接口都新建HttpClient但复用同一个底层Handler,前两个请求占用连接后未及时释放资源,第三个请求等待连接超时,就会抛出TaskCanceledException——HttpClient的超时异常统一包装为这个异常类型,和主动取消任务抛出的异常类型一致。 - Unity生命周期异步写法错误:Unity不会主动跟踪
async Task签名的生命周期方法(比如Awake/Start)返回的Task,加上代码用了ConfigureAwait(false)让await之后的逻辑跑在线程池,脱离了Unity主线程上下文,一方面会让非主线程访问UI组件(比如给Status.text赋值)触发隐式异常,另一方面Unity对象生命周期结束时会直接取消未被跟踪的Task,放大了连接超时的问题。
修复步骤
按以下顺序修改即可解决问题:
- 调整生命周期异步写法
把Awake方法签名改成async void Awake(),Unity原生对生命周期方法的async void签名有正确的跟踪支持;去掉所有接口调用的ConfigureAwait(false),保证await之后逻辑回到主线程执行,避免跨线程访问Unity对象:async void Awake() { await GetPerformanceInfo(); await GetProductionInfo(); await GetChecklistInfo(); } - 修复HttpClient连接池限制
初始化ApiHelpers.HttpClientHandler时显式设置同域名最大并发连接数,覆盖UWP默认的2连接限制:
额外优化:不要每次调用接口都新建// 在全局初始化逻辑中配置Handler var httpHandler = new HttpClientHandler(); // 根据业务需求设置并发数,设置为10足够覆盖常规多接口调用场景 httpHandler.MaxConnectionsPerServer = 10; ApiHelpers.HttpClientHandler = httpHandler;HttpClient实例,全局复用同一个HttpClient即可,减少资源释放不及时的问题。 - 异常快速校验
捕获TaskCanceledException时可以加日志判断异常来源:如果ex.CancellationToken.IsCancellationRequested为false,就可以确认是请求超时/连接不足导致的异常,而非代码主动触发的取消,方便后续排查。
内容的提问来源于stack exchange,提问作者Alain Cuypers
相关产品推荐
相关产品推荐

