C#高性能跳出多层嵌套函数的最优方案探讨
背景概述
我开发了一款C#自动化程序,用于控制浏览器、桌面应用等外部程序。程序核心逻辑通过外层循环轮询应用最新状态,判断是否需要执行对应操作,基础结构如下:
void OuterMostFunc() { while(!exit) { InquiryLatestOuterAppInfo(); if(<NeedPerform>) { //InitSomethings... DirectingFunc(); //HandleSomethings... } Thread.Sleep(milliseconds); } } void DirectingFunc() { if(A_Page_Cond) A_Page_Func(); else if(B_Page_Cond) B_Page_Func(); ... }
但外部应用可能被手动操作或遭遇意外导致状态变更,必须在多个关键点检查状态,一旦变更就跳出到最外层重新评估。
原有方案1:自定义异常+Try-Catch中断
此前采用的方案是通过自定义BreakException结合Try-Catch实现任意层级快速中断到最外层,代码如下:
void OuterMostFunc() { while(!exit) { InquiryLatestOuterAppInfo(); if(<NeedPerform>) { //InitSomethings... try { DirectingFunc(); } catch(BreakExcetion ex) {} //HandleSomethings... } Thread.Sleep(milliseconds); } } void Any_Deep_Level_Func() { //DoSomethings. if(CheckPageIsChanged()) throw new BreakException(); //DoSomethings. }
该方案优势是逻辑优雅,可从任意深层函数直接跳回最外层,且外层能统一重置变量;但Try-Catch带来的数十毫秒级性能开销和高CPU负载,已无法满足当前场景对速度的极高要求。
原有方案2:返回值检查中断
若改为让每个函数返回bool(或使用out bool),调用后检查是否需要中断,虽能规避性能问题,但会导致代码可读性严重下降——尤其是程序实际函数层级多、逻辑复杂的情况下,每调用一个函数都要加判断,代码冗余度极高。
待评估方案:CancellationTokenSource分层检查
现在考虑传递CancellationTokenSource给所有非单元操作函数,内层检查到状态变更时取消令牌,上层函数调用后检查令牌状态决定是否返回,代码如下:
void OuterMostFunc() { while (!exit) { InquiryLatestOuterAppInfo(); if(<NeedPerform>) { //InitSomethings... CancellationTokenSource cts = new CancellationTokenSource(); DirectingFunc(cts); //HandleSomethings... } } } void DirectingFunc(CancellationTokenSource cts) { if(A_Page_Cond) A_Page_Func(cts); else if(B_Page_Cond) B_Page_Func(cts); } void A_Page_Func(CancellationTokenSource cts) { //DoSomethings. XX_Func(cts); if (cts.IsCancellationRequested) return; //DoSomethings. } void XX_Func(CancellationTokenSource cts) { //DoSomethings. YY_Func(cts); if (cts.IsCancellationRequested) return; //DoSomethings. } void YY_Func(CancellationTokenSource cts) { //DoSomethings. if (CheckPageIsChanged()) { cts.Cancel(); return; } //DoSomethings. }
方案评估
这个方案确实比返回值检查方案更简洁,同时解决了异常方案的性能问题,适配当前场景需求:
- 性能优势:完全规避Try-Catch的开销,符合“状态变更后执行速度优先”的优先级要求
- 可读性优势:相比返回值方案,不需要每个函数都返回状态标识,仅需传递令牌并在关键调用后检查,代码冗余度大幅降低
- 控制权符合要求:内层函数可自主检查状态并发起取消,避免外层干预引发的操作冲突
唯一的小缺点是所有相关函数都需要添加CancellationTokenSource参数,会增加函数签名的复杂度,但相比返回值方案的大量重复判断,这种复杂度的提升是可接受的。
如果想进一步优化,可以考虑传递CancellationToken而非CancellationTokenSource给不需要主动取消的函数,仅让需要发起取消的内层函数持有CancellationTokenSource,这样能避免上层函数误操作取消令牌,提升代码安全性。
内容的提问来源于stack exchange,提问作者SAng

