C#实现PLC事件请求串行处理:解决并发执行问题
PLC请求串行处理改造
我正在与外部PLC设备通信,PLC主动发起数据请求。我通过监听PLC变量值变化(如NEEDNEWPOSITION或NEEDNEWBARVALUES)来响应请求,但目前偶尔会出现两个请求同时执行的情况(推测是不同请求的处理耗时差异导致)。希望修改代码实现请求的串行处理,已知async/await、Task这类异步方案,但认为实现成本过高,现有代码如下:
private void PLCValueChanged(object sender, EventArgs e) { bool xDisplayValue = false; PLCVar plcvariable = (PLCVar)sender; string VarName = plcvariable.DisplayName; switch (VarName) { case "NEEDNEWPOSITION": //Writing required position to PLC if (Convert.ToBoolean(plcvariable.Value)) { SearchNewPosition(); OPCclient.SendVarToPLC(OPCclient.SendPlcAllBarsFinished, "FALSE"); OPCclient.SendVarToPLC(OPCclient.SendPLCAllMagnetsFinished, "FALSE"); MagnetsInArea = GetMagnetsInWorkArea(); SymbolsInArea = GetSymbolsInWorkArea(); BarsInArea = GetBarsInWorkArea(); } else { OPCclient.SendVarToPLC(OPCclient.SendPLCNewPositionIsSend, "FALSE"); } break; case "NEEDNEWBARVALUES": //Writing Bar Values to PLC if (Convert.ToBoolean(plcvariable.Value)) { OPCclient.SendVarToPLC(OPCclient.SendPLCBarStrippedOK, "FALSE"); OPCclient.SendVarToPLC(OPCclient.SendPLCBarMagnetsOK, "FALSE"); OPCclient.SendVarToPLC(OPCclient.SendPLCAllBarMagnetsLoose, "FALSE"); SetFirstBarValues(); OffsetsCalculated = false; StartVisualisation?.Invoke(this, null); //%M59 } else //if (!Convert.ToBoolean(plcvariable.Value)) { OPCclient.SendVarToPLC(OPCclient.SendPLCBarDataIsSend, "FALSE"); } break; } }
低成本串行处理方案:队列+后台线程
不需要引入复杂的异步框架,用请求队列+后台线程即可实现串行处理,改动量小:
- 定义请求枚举和队列:
// 标识请求类型 private enum PLCRequestType { NeedNewPosition_True, NeedNewPosition_False, NeedNewBarValues_True, NeedNewBarValues_False } // 线程安全队列存储请求 private readonly ConcurrentQueue<PLCRequestType> _plcRequestQueue = new ConcurrentQueue<PLCRequestType>(); // 后台处理线程 private Thread _requestProcessingThread; // 标识线程是否运行 private bool _isProcessingThreadRunning;
- 初始化后台线程(在类的构造函数或初始化方法中):
public YourClassName() // 替换为你的类名 { _isProcessingThreadRunning = true; _requestProcessingThread = new Thread(ProcessPLCRequests) { IsBackground = true, // 后台线程随程序退出自动终止 Priority = ThreadPriority.Normal }; _requestProcessingThread.Start(); }
- 实现队列处理逻辑:
private void ProcessPLCRequests() { while (_isProcessingThreadRunning) { if (_plcRequestQueue.TryDequeue(out var requestType)) { HandlePLCRequest(requestType); } else { // 队列为空时短暂休眠,避免占用过多CPU Thread.Sleep(10); } } } // 原事件中的处理逻辑移到这里 private void HandlePLCRequest(PLCRequestType requestType) { switch (requestType) { case PLCRequestType.NeedNewPosition_True: SearchNewPosition(); OPCclient.SendVarToPLC(OPCclient.SendPlcAllBarsFinished, "FALSE"); OPCclient.SendVarToPLC(OPCclient.SendPLCAllMagnetsFinished, "FALSE"); MagnetsInArea = GetMagnetsInWorkArea(); SymbolsInArea = GetSymbolsInWorkArea(); BarsInArea = GetBarsInWorkArea(); break; case PLCRequestType.NeedNewPosition_False: OPCclient.SendVarToPLC(OPCclient.SendPLCNewPositionIsSend, "FALSE"); break; case PLCRequestType.NeedNewBarValues_True: OPCclient.SendVarToPLC(OPCclient.SendPLCBarStrippedOK, "FALSE"); OPCclient.SendVarToPLC(OPCclient.SendPLCBarMagnetsOK, "FALSE"); OPCclient.SendVarToPLC(OPCclient.SendPLCAllBarMagnetsLoose, "FALSE"); SetFirstBarValues(); OffsetsCalculated = false; StartVisualisation?.Invoke(this, null); //%M59 break; case PLCRequestType.NeedNewBarValues_False: OPCclient.SendVarToPLC(OPCclient.SendPLCBarDataIsSend, "FALSE"); break; } }
- 修改原事件处理方法,仅负责入队请求:
private void PLCValueChanged(object sender, EventArgs e) { PLCVar plcvariable = (PLCVar)sender; string VarName = plcvariable.DisplayName; bool isValueTrue = Convert.ToBoolean(plcvariable.Value); switch (VarName) { case "NEEDNEWPOSITION": _plcRequestQueue.Enqueue(isValueTrue ? PLCRequestType.NeedNewPosition_True : PLCRequestType.NeedNewPosition_False); break; case "NEEDNEWBARVALUES": _plcRequestQueue.Enqueue(isValueTrue ? PLCRequestType.NeedNewBarValues_True : PLCRequestType.NeedNewBarValues_False); break; } }
- 可选:在类销毁时停止后台线程(如Dispose方法):
public void Dispose() { _isProcessingThreadRunning = false; _requestProcessingThread?.Join(1000); // 等待线程终止,超时则强制结束 }
方案优势
- 改动量小:原处理逻辑几乎无需修改,仅需迁移到单独方法
- 无异步框架依赖:不需要重构为async/await模式,学习成本低
- 严格串行:后台线程逐个处理队列请求,彻底避免并发执行问题
内容的提问来源于stack exchange,提问作者Bart
相关产品推荐
相关产品推荐

