这段代码中存在一个非常典型的“异步转同步”的阻塞等待模式使用while循环和Thread.Sleep强行等待异步回调结果。为了让代码更优雅、可复用并且避免Thread.Sleep阻塞主线程我们可以将这个等待逻辑抽离成一个通用的、异步的或同步的根据你需求公共方法。这里提供两种设计方案方案一推荐的现代异步async/await方案不阻塞线程和方案二保持原有逻辑的传统同步方案。方案一通用异步等待方法推荐 在现代 C# 开发中使用Task.Delay代替Thread.Sleep可以释放 CPU 线程大幅提升系统并发性能特别是在 PLC/MES 这种多托盘并发场景下。1. 提取出的公共等待方法你可以将这个方法放到一个工具类如WaitHelper或当前类的私有/公共函数中usingSystem;usingSystem.Collections.Concurrent;usingSystem.Threading.Tasks;publicstaticclassWaitHelper{/// summary/// 通用的异步条件等待方法带超时和自定义退出条件/// /summary/// typeparam nameTKey字典键类型/typeparam/// typeparam nameTValue字典值类型/typeparam/// param namecacheDic存储结果的并发字典/param/// param namekey要监控的键如托盘号/param/// param nametimeoutSeconds超时时间秒/param/// param nameextraBreakCondition外部中止条件例如状态变为IDLE时触发中断/param/// param namecheckIntervalMs检查轮询间隔毫秒默认500ms/param/// returns返回获取到的结果若超时或异常中断返回 null/returnspublicstaticasyncTaskTValueWaitForResultAsyncTKey,TValue(ConcurrentDictionaryTKey,TValuecacheDic,TKeykey,inttimeoutSeconds,FuncboolextraBreakConditionnull,intcheckIntervalMs500)whereTValue:class{DateTimestartTimeDateTime.Now;// 当字典中包含这个键且满足外部继续执行的条件时进行循环while(cacheDic.ContainsKey(key)){// 检查外部状态是否要求中止如EAP被禁用、设备突然变更为IDLE等if(extraBreakCondition!nullextraBreakCondition()){break;}// 检查字典里的值是否已经被填充有结果了if(cacheDic.TryGetValue(key,outTValuevalue)value!null){// 成功获取到结果从字典中移除并返回cacheDic.TryRemove(key,out_);returnvalue;}// 超时检查if((DateTime.Now-startTime).TotalSecondstimeoutSeconds){break;// 触发超时跳出循环}// 异步等待不阻塞当前线程awaitTask.Delay(checkIntervalMs);}returnnull;// 超时或被外部条件中断返回 null}}2. 重构后的原方法调用方式使用上述异步方法后你的原方法需要改为async void或async TaskprivateasyncvoidOnlineModeTrayScanProcessResult(SignalObjectsignal,IPLCBizHelperpLCBizHelper,stringcurrentLoadCarriarSN){// 1. 初始化字典this.TrayChecks.AddOrUpdate(currentLoadCarriarSN,tnull,(k,v)null);// 2. 触发异步校验任务不需要用 var task 变量接收_Task.Run(async(){varobj2awaitthis.m_EAPHelper.CarriarCheckIn(currentLoadCarriarSN);// 注意你原本的代码里拿到 obj2 后并没有赋值回 TrayChecks// 实际业务中此处应该把结果写入this.TrayChecks[currentLoadCarriarSN] obj2.Result...});// 默认结果varonlineCheckResult1;inttimeoutSeconds30;// 3. 调用公共异步等待方法// 将外部环境的状态作为 Lambda 表达式传入stringwaitResultawaitWaitHelper.WaitForResultAsync(this.TrayChecks,currentLoadCarriarSN,timeoutSeconds,extraBreakCondition:()!this.m_EAP_Enable||this.ProductionStateProductionState.IDLE||this.ProductionStateProductionState.Ready);// 4. 判断是拿到结果还是超时了if(waitResult!null){onlineCheckResultwaitResult;}else{// 如果字典里还存在这个键说明是因为超时或状态改变退出的Log.Error($托盘{currentLoadCarriarSN}EAP 校验超时或状态异常);OnLog?.Invoke(this,ErrorLevel.Warning,$托盘{currentLoadCarriarSN}EAP 校验超时或状态异常);}// 5. 给自动化发处理结果if(Enum.TryParse(onlineCheckResult,outTrackInResultresult)){SendTrayScanProcessResult(signal,pLCBizHelper,currentLoadCarriarSN,result,Convert.ToInt32(pLCBizHelper.TargetDeviceNo));}}方案二传统同步等待方法保持 Thread.Sleep如果你不希望改动原方法的private void签名不想引入async/await可以使用这种纯同步阻塞的提取方式。1. 提取出的公共同步方法publicstaticclassWaitHelper{/// summary/// 通用的同步阻塞条件等待方法/// /summarypublicstaticTValueWaitForResultTKey,TValue(ConcurrentDictionaryTKey,TValuecacheDic,TKeykey,inttimeoutSeconds,FuncboolextraBreakConditionnull,intcheckIntervalMs500)whereTValue:class{DateTimestartTimeDateTime.Now;while(cacheDic.ContainsKey(key)){if(extraBreakCondition!nullextraBreakCondition()){break;}if(cacheDic.TryGetValue(key,outTValuevalue)value!null){cacheDic.TryRemove(key,out_);returnvalue;}if((DateTime.Now-startTime).TotalSecondstimeoutSeconds){break;}Thread.Sleep(checkIntervalMs);// 阻塞当前线程}returnnull;}}2. 原方法调用方式privatevoidOnlineModeTrayScanProcessResult(SignalObjectsignal,IPLCBizHelperpLCBizHelper,stringcurrentLoadCarriarSN){this.TrayChecks.AddOrUpdate(currentLoadCarriarSN,tnull,(k,v)null);Task.Run(async(){varobj2awaitthis.m_EAPHelper.CarriarCheckIn(currentLoadCarriarSN);});varonlineCheckResult1;inttimeoutSeconds30;// 同步调用直接阻塞等待stringwaitResultWaitHelper.WaitForResult(this.TrayChecks,currentLoadCarriarSN,timeoutSeconds,extraBreakCondition:()!this.m_EAP_Enable||this.ProductionStateProductionState.IDLE||this.ProductionStateProductionState.Ready);if(waitResult!null){onlineCheckResultwaitResult;}else{Log.Error($托盘{currentLoadCarriarSN}EAP 校验超时);OnLog?.Invoke(this,ErrorLevel.Warning,$托盘{currentLoadCarriarSN}EAP 校验超时);}if(Enum.TryParse(onlineCheckResult,outTrackInResultresult)){SendTrayScanProcessResult(signal,pLCBizHelper,currentLoadCarriarSN,result,Convert.ToInt32(pLCBizHelper.TargetDeviceNo));}} 代码优化小建议避坑你在Task.Run中调用了await this.m_EAPHelper.CarriarCheckIn(currentLoadCarriarSN)但是拿到的obj2并没有写回到this.TrayChecks字典中。正常情况下MES/EAP 收到结果后应该在某个地方执行this.TrayChecks[currentLoadCarriarSN]某个结果码(如0或1);如果CarriarCheckIn本身不会自动去更新TrayChecks记得在Task.Run里面加上赋值逻辑否则这个等待方法会百分之百触发超时。