AutoBizDriver 多工站、多硬件工况下不加锁的风险分析
·
多工站、多硬件工况下不加锁的风险分析
该代码涉及多个并发源:
- 多个
IPLCBizHelper实例可能在不同线程中触发事件(如扫码完成、上料完成、下料完成)。 Task.Run启动的异步任务(EAP 交互、清料等)与主流程并行执行。StartLot/EndLot中并行启动多个业务对象的初始化/结批。- 事件回调中可能递归调用(如
OnCarriarArrive内部调用自身)。
不加锁必然导致数据竞争和不一致。以下是具体风险点及示例:
1. ProductionSiteAbleQueue_MaxWell – 队列并发破坏
Dictionary<string, Queue<ProductionSite>> ProductionSiteAbleQueue_MaxWell
- 写操作:
StartLot中初始化Enqueue;OnCarriarArrive中Dequeue;OnUnloadDone_MaxWell中Enqueue;OnLoadDone_MaxWell中失败时Enqueue。 - 并发场景:两个托盘同时到达不同设备,触发两个
OnCarriarArrive并行执行,同时对一个设备队列Dequeue,可能抛出InvalidOperationException(队列空)或导致计数错误。 - 不加锁后果:队列数据损坏,托盘分配错误,导致设备死锁或物料丢失。
2. ProducedCarrierSN – Dictionary 多线程写
Dictionary<string, int> ProducedCarrierSN
- 在
OnCommonEvent__PLC的扫码处理分支中,当TargetDevice == currentDeviceNo时执行Add。 - 多个 PLC 设备可能同时完成扫码,并行向同一个
Dictionary添加不同键,未加锁会引发ArgumentException(键已存在)或内部状态损坏。
3. DataRecord 与 SNtoIndexMap – DataTable 非线程安全
OnLoadDone_MaxWell中调用DataRecord.Rows.Add()并更新SNtoIndexMap。EOT中通过DataRecord.Select()查询并修改行数据。- 多个测试机同时完成测试,并发写入
DataTable会导致IndexOutOfRangeException或数据混乱。
4. 布尔标志位 – 可见性与乱序执行
private bool m_CurrentLoadSignalDone = true;
private bool m_EndlotDoing = false;
private bool ClearProductionDone = true;
- 多个线程读写这些标志(例如
OnCarriarArrive中设置为false,OnLoadDone_MaxWell中设置为true)。 - 没有
volatile或内存屏障,可能导致一个线程的修改对另一个线程不可见,从而出现重复上料或死等。
5. 集合 m_StartLotDoneList / m_EndLotDoneList
OnCommonEvent__EQP中Add,Run/EndLot中遍历检查Count。- 并发
Add可能破坏List内部数组,且Count检查不准确。
优化意见
一、强制使用线程安全集合或加锁保护
1. 队列改用 ConcurrentQueue + ConcurrentDictionary
private ConcurrentDictionary<string, ConcurrentQueue<ProductionSite>> ProductionSiteAbleQueue_MaxWell;
TryDequeue原子操作,无需显式锁。- 但需注意:
ConcurrentQueue是无界的,对于有容量限制的缓存位需要额外控制。
2. ProducedCarrierSN → ConcurrentDictionary<string, int>
private ConcurrentDictionary<string, int> ProducedCarrierSN = new();
- 使用
TryAdd代替Add,避免异常。
3. DataRecord 与 SNtoIndexMap 加锁保护
private readonly object _dataLock = new object();
lock (_dataLock)
{
DataRecord.Rows.Add(row);
SNtoIndexMap[sn] = index;
}
// 同样在 EOT 中 lock 查询和更新
- 或使用
ConcurrentBag存储 DUT 快照,UI 更新时再转 DataTable。
4. 简单状态标志使用 Interlocked 或 volatile
private int _currentLoadSignalDone = 1; // 1=true, 0=false
Interlocked.Exchange(ref _currentLoadSignalDone, 0);
if (Interlocked.CompareExchange(ref _currentLoadSignalDone, 1, 0) == 0) ...
- 或者使用
volatile bool配合内存屏障(更简单但不适用于读-修改-写)。
5. 列表改用 ConcurrentBag 或加锁
private ConcurrentBag<string> m_StartLotDoneList = new();
// 或者 private readonly object _lotLock = new object();
二、避免在事件回调中执行阻塞/耗时操作
问题代码示例(OnCommonEvent__PLC 中直接弹窗):
Runtime.MainForm.Invoke(new Action(() =>
{
m_LoadCarriarSNScanNGForm.ShowDialog(); // 阻塞等待用户输入
}));
- 这会挂起 PLC 事件处理线程,导致后续信号无法及时响应,可能触发 PLC 超时。
优化:
- 将弹窗操作放到独立线程(
Task.Run),回调完成立即返回,后续处理通过信号机制或再次触发事件完成。
三、消除递归调用和潜在死循环
OnCarriarArrive 结尾调用自身:
OnCarriarArrive(sender, null);
- 如果队列为空或条件不满足,可能形成紧循环,消耗 CPU。
- 应改为设置标志或使用定时器延迟重试,而非立即递归。
IsAllowProduceByCurrentDevice 中的 goto IsNextDeviceIsAllProcessing
- 逻辑混乱,嵌套循环与标签降低可读性。建议重构为循环方法。
四、异步化长时间操作
OnCarriarArrive 中的 while 等待 BeforeSOT:
while (ok != 0)
{
// 循环重试,每次 Sleep(1000)
}
- 阻塞 PLC 回调线程,影响整线响应。
- 优化:异步轮询 + 状态机,或使用
TaskCompletionSource等待设备就绪信号。
五、明确线程模型 – 建议使用单一事件处理管道
- 当前各 PLC/EQP 事件直接在回调中执行业务逻辑,线程不可控。
- 优化:引入一个队列(如
ConcurrentQueue<Action>)和单线程消费者,所有业务操作(上料、下料、状态变更)投递到此队列顺序执行。可极大简化并发控制。 - 示例如下:
private BlockingCollection<Action> _workQueue = new();
private Task _workerTask;
void StartWorker() => _workerTask = Task.Run(() => {
foreach (var action in _workQueue.GetConsumingEnumerable())
action();
});
void Enqueue(Action action) => _workQueue.Add(action);
- 所有事件回调只做
Enqueue(() => { 实际逻辑 })。
六、其他建议
- 日志增强:在关键操作入口打印线程 ID,便于排查竞态。
- 用
CancellationTokenSource代替while(状态)+Thread.Sleep。 - EAP 回调
M_EAPHelper_OnMessageReceived已经异步,但修改ConcurrentDictionary没问题,不过注意内部Invoke到 UI 线程可能死锁(应使用BeginInvoke)。 - 参数解构:
Dictionary<string, Object>频繁装箱拆箱,可定义强类型参数类。 - 状态机重构:
ProductionState有多个状态,但切换时未检查合法性,可引入状态机类。
总结
不加锁绝对不可以。该代码在真实多工站并发场景下会频繁崩溃或产生逻辑错误。最低限度的修改是:
- 将所有共享集合替换为
ConcurrentDictionary/ConcurrentQueue。 - 对
DataTable、List等非安全集合加lock。 - 将递归调用改为定时轮询或事件驱动。
- 移除事件回调中的阻塞操作。
若要长期稳定运行,强烈建议重构为单线程事件循环模型,彻底消除并发复杂性。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)