news 2026/5/31 14:51:46

将这个等待逻辑抽离成一个通用的、异步的(或同步的,根据你需求)公共方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
将这个等待逻辑抽离成一个通用的、异步的(或同步的,根据你需求)公共方法

这段代码中存在一个非常典型的“异步转同步”的阻塞等待模式(使用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 name="TKey">字典键类型</typeparam>/// <typeparam name="TValue">字典值类型</typeparam>/// <param name="cacheDic">存储结果的并发字典</param>/// <param name="key">要监控的键(如托盘号)</param>/// <param name="timeoutSeconds">超时时间(秒)</param>/// <param name="extraBreakCondition">外部中止条件(例如:状态变为IDLE时触发中断)</param>/// <param name="checkIntervalMs">检查轮询间隔(毫秒),默认500ms</param>/// <returns>返回获取到的结果,若超时或异常中断返回 null</returns>publicstaticasyncTask<TValue>WaitForResultAsync<TKey,TValue>(ConcurrentDictionary<TKey,TValue>cacheDic,TKeykey,inttimeoutSeconds,Func<bool>extraBreakCondition=null,intcheckIntervalMs=500)whereTValue:class{DateTimestartTime=DateTime.Now;// 当字典中包含这个键,且满足外部继续执行的条件时,进行循环while(cacheDic.ContainsKey(key)){// 检查外部状态是否要求中止(如:EAP被禁用、设备突然变更为IDLE等)if(extraBreakCondition!=null&&extraBreakCondition()){break;}// 检查字典里的值是否已经被填充(有结果了)if(cacheDic.TryGetValue(key,outTValuevalue)&&value!=null){// 成功获取到结果,从字典中移除并返回cacheDic.TryRemove(key,out_);returnvalue;}// 超时检查if((DateTime.Now-startTime).TotalSeconds>timeoutSeconds){break;// 触发超时,跳出循环}// 异步等待,不阻塞当前线程awaitTask.Delay(checkIntervalMs);}returnnull;// 超时或被外部条件中断,返回 null}}

2. 重构后的原方法调用方式

使用上述异步方法后,你的原方法需要改为async voidasync Task

privateasyncvoidOnlineModeTrayScanProcessResult(SignalObjectsignal,IPLCBizHelperpLCBizHelper,stringcurrentLoadCarriarSN){// 1. 初始化字典this.TrayChecks.AddOrUpdate(currentLoadCarriarSN,t=>null,(k,v)=>null);// 2. 触发异步校验任务(不需要用 var task 变量接收)_=Task.Run(async()=>{varobj2=awaitthis.m_EAPHelper.CarriarCheckIn(currentLoadCarriarSN);// 注意:你原本的代码里拿到 obj2 后并没有赋值回 TrayChecks,// 实际业务中,此处应该把结果写入:this.TrayChecks[currentLoadCarriarSN] = obj2.Result...});// 默认结果varonlineCheckResult="1";inttimeoutSeconds=30;// 3. 调用公共异步等待方法// 将外部环境的状态作为 Lambda 表达式传入stringwaitResult=awaitWaitHelper.WaitForResultAsync(this.TrayChecks,currentLoadCarriarSN,timeoutSeconds,extraBreakCondition:()=>!this.m_EAP_Enable||this.ProductionState==ProductionState.IDLE||this.ProductionState==ProductionState.Ready);// 4. 判断是拿到结果还是超时了if(waitResult!=null){onlineCheckResult=waitResult;}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>/// 通用的同步阻塞条件等待方法/// </summary>publicstaticTValueWaitForResult<TKey,TValue>(ConcurrentDictionary<TKey,TValue>cacheDic,TKeykey,inttimeoutSeconds,Func<bool>extraBreakCondition=null,intcheckIntervalMs=500)whereTValue:class{DateTimestartTime=DateTime.Now;while(cacheDic.ContainsKey(key)){if(extraBreakCondition!=null&&extraBreakCondition()){break;}if(cacheDic.TryGetValue(key,outTValuevalue)&&value!=null){cacheDic.TryRemove(key,out_);returnvalue;}if((DateTime.Now-startTime).TotalSeconds>timeoutSeconds){break;}Thread.Sleep(checkIntervalMs);// 阻塞当前线程}returnnull;}}

2. 原方法调用方式

privatevoidOnlineModeTrayScanProcessResult(SignalObjectsignal,IPLCBizHelperpLCBizHelper,stringcurrentLoadCarriarSN){this.TrayChecks.AddOrUpdate(currentLoadCarriarSN,t=>null,(k,v)=>null);Task.Run(async()=>{varobj2=awaitthis.m_EAPHelper.CarriarCheckIn(currentLoadCarriarSN);});varonlineCheckResult="1";inttimeoutSeconds=30;// 同步调用,直接阻塞等待stringwaitResult=WaitHelper.WaitForResult(this.TrayChecks,currentLoadCarriarSN,timeoutSeconds,extraBreakCondition:()=>!this.m_EAP_Enable||this.ProductionState==ProductionState.IDLE||this.ProductionState==ProductionState.Ready);if(waitResult!=null){onlineCheckResult=waitResult;}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里面加上赋值逻辑,否则这个等待方法会百分之百触发超时

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/29 14:03:57

量子算法在优化与模拟中的前沿应用与挑战

1. 量子算法在优化与模拟中的前沿挑战量子计算领域正在经历一场静默的革命。作为一名长期跟踪量子算法发展的研究者&#xff0c;我亲眼目睹了过去五年间量子优化和模拟算法从理论构想逐步走向实际应用的历程。2024年5月举办的美国国家量子计划联合算法研讨会汇集了学术界、国家…

作者头像 李华
网站建设 2026/5/29 14:00:58

别再死记硬背公式了!用MATLAB R2023b画图,5分钟搞懂贝塞尔函数的震荡特性

用MATLAB可视化解锁贝塞尔函数的物理直觉贝塞尔函数在工程数学中就像一位熟悉的陌生人——它的名字频繁出现在波动方程、热传导和电磁场问题中&#xff0c;但大多数教科书只用冰冷的积分表达式和递推公式来定义它。当我第一次在光纤模式分析中遇到Jₙ(x)时&#xff0c;那些抽象…

作者头像 李华
网站建设 2026/5/29 13:55:39

Taotoken用量看板功能详解与日常成本监控实践

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 Taotoken用量看板功能详解与日常成本监控实践 对于使用大模型API的开发者或团队而言&#xff0c;清晰、及时地了解资源消耗和成本构…

作者头像 李华
网站建设 2026/5/29 13:53:51

Claude蒙特卡洛模拟全链路拆解:从Prompt概率约束、样本去相关性处理到收敛性验证(含8张权威收敛诊断图)

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;Claude蒙特卡洛模拟全链路概览 Claude模型本身并非专为数值模拟设计&#xff0c;但其强大的推理与代码生成能力可作为蒙特卡洛模拟工作流的智能编排中枢——从问题建模、随机采样策略生成、到结果后处理…

作者头像 李华