.NET 5下ConcurrentBag多线程应用竞态条件排查及设计模式优化问询
多设备异步处理流程优化问题
背景
过去几个月我一直在尝试优化一个使用DispatcherTimer定期检查资源是否需要更新/处理的流程:资源(即Product)更新后,要将其移动到下一处理步骤,而资源不一定能立即就绪。
我遇到的阻碍分为两点:
- 当前流程为同步实现,我希望改造为异步架构
- 我卡壳的模块看起来是很常见的设计模式,但我无法简洁描述问题,因此无法从搜索引擎获取有效解决方案
需要特别说明的是,我通过直连USB访问这些Product,使用LibUsbDotNet完成设备交互,且已经将USB连接改为异步实现,可同时连接多个Product、并行处理任意数量的设备。
相关代码
public class Product { public bool IsSoftwareUpdated = false; public bool IsProductInformationCorrect = false; public bool IsEOLProcessingCompleted = false; public Product(){} ~Product(){} } public class ProcessProduct { List<Product> bagOfProducts = new List<Product>(new Product[10]); ConcurrentBag<Product> UnprocessedUnits = new ConcurrentBag<Product>(); ConcurrentBag<Product> CurrentlyUpdating = new ConcurrentBag<Product>(); ConcurrentBag<Product> CurrentlyVerifyingInfo = new ConcurrentBag<Product>(); ConcurrentBag<Product> FinishedProcessing = new ConcurrentBag<Product>(); DispatcherTimer _timer = new DispatcherTimer(); public ProcessProduct() { _timer.Tick += Timer_Tick; //每1秒调用Timer_Tick _timer.Interval = new TimeSpan(0,0,1); //1秒定时器 bagOfProducts.ForEach(o => UnprocessedUnits.Add(o)); //将所有产品放入未处理队列 StartProcessing(); } private void StartProcessing() { _timer.Start(); } private void Timer_Tick(object sender, EventArgs e) { ProductOrganizationHandler(); foreach(Product prod in CurrentlyUpdating.ToList()) { UpdateProcessHandler(prod); //使用await的异步函数 } foreach(Product prod in CurrentlyVerifyingInfo.ToList()) { VerifyingInfoHandler(prod); //使用await的异步函数 } if(FinishedProcessing.Count == bagOfProducts.Count) { _timer.Stop(); //所有产品处理完成后停止流程 } } private void ProductOrganizationHandler() { //逐个从各ConcurrentBag中取出Product,根据当前处理完成的步骤移动到对应队列 //如果当前步骤未完成则放回原队列 //例如:所有未处理单元从UnprocessUnits移动到CurrentlyUpdating或CurrentlyVerifying等 //如果产品完成更新,则从CurrentlyUpdating移动到CurrentlyVerifying或FinishedProcessing } private async void UpdateProcessHandler(Product prod) { await Task.Delay(1000).ConfigureAwait(false); //实际逻辑:验证USB通信,执行USB更新 } private async void VerifyingInfoHandler(Product prod) { await Task.Delay(1000).ConfigureAwait(false); //实际逻辑:执行处理逻辑,通过USB与产品通信 } }
咨询问题
- 这段代码是否存在会造成实际影响的竞态条件?尤其是
ProductOrganizationHandler()逻辑,以及Timer_Tick()中遍历ConcurrentBag的逻辑,毕竟Timer_Tick()每秒触发一次。目前这段代码大多数场景下能正常运行,但我担心如果出现ProductOrganizationHandler()执行耗时超过1秒的极端情况,会触发难以排查的偶发竞态条件bug。 - 这类流程有没有更适配的设计模式?C#是我接触的第一门面向对象语言,我都是在工作中自学的,我的日常工作大多是嵌入式C开发,没有系统学习过面向对象设计模式。
核心需求
当USB设备就绪时,异步完成每台设备的更新、校验、通信操作,当列表内所有产品都处理完成(或触发超时)后结束流程,本项目基于.NET 5开发。
补充解决方案
后续遇到同类问题的开发者可参考该方案:
我之前不了解DispatcherTimer的Tick事件是提交到Dispatcher队列中执行的,也就是说如果当前有Tick事件正在运行,下一个Tick会等待上一个执行完成后才会启动,Timer_Tick本身是单线程串行执行的,不会并发运行。因此我之前担心的几乎所有多线程/并发问题都不存在,我可以将Timer_Tick当做单线程非并发函数处理。另外为了避免Tick事件堆积,我在Timer_Tick开头先执行_timer.Stop(),在方法执行结束后再重启定时器。
内容的提问来源于stack exchange,提问作者StephenKercher
相关产品推荐
相关产品推荐

