Blazor Server多客户端读取远程PLC数据异常问题求助
问题描述
我在Blazor Server应用中用第三方DLL连接远程PLC读取数据,该DLL的doNCK_ReadMixEx方法仅支持静态变量(不用静态的话读不出数据)。我把这个方法做成Scoped服务,想让每个客户端能同时周期性(每秒一次)读取不同PLC的数据。
单客户端读取完全正常,但两个客户端同时读不同PLC时,数据读不全,还频繁出现乱码或错误值。不确定是方法调用问题还是静态变量导致的,代码如下:
Razor页面(触发周期性读取)
@page "/PLC_Status" @inject Deltalogic Deltalogic @inject Parameter_840D Parameter_840D <button @onclick="Read_CNC_Status">Start Reading</button>
Razor.cs
public async Task Read_CNC_Status() { Reading_active = true; for (int k=0; k<=200;k++) { Read_PLC_Data(); InvokeAsync(() => { StateHasChanged(); }); await Task.Delay(1000); if (k == 200) { Stop_CNC_Status(); } } } public void Read_PLC_Data() //Here the doNCK_ReadMixEx is called for each PLC parameter { Parameter_840D.Par_840D_Channel_Status(Deltalogic.channel_no); //Pick-up parameters for variable-1 Deltalogic.doNCK_ReadMixEx(Deltalogic.MAE_Status_DevNr, 5000, out Deltalogic.MAE_Status_Mode); Parameter_840D.Par_840D_Prog_Status(Deltalogic.channel_no); //Pick-up parameters for variable-2 Deltalogic.doNCK_ReadMixEx(Deltalogic.MAE_Status_DevNr, 5000, out Deltalogic.MAE_Status_State_CH[1]); Parameter_840D.Par_840D_Machine_Active_Workpiece(Deltalogic.channel_no);//Pick-up parameters for variable-3 Deltalogic.doNCK_ReadMixEx(Deltalogic.MAE_Status_DevNr, 5000, out Deltalogic.MAE_Status_WPD_CH[Deltalogic.channel_no]); // and other PLC parameters.... }
Parameter_840D(doNCK_ReadMixEx参数配置Scoped服务)
// The parameters of doNCK_ReadMixEx have to be defined as static. If not, data cannot be read public class Parameter_840D { [Inject] Deltalogic Deltalogic { get; set; } public static byte channel_no = 1; public static ushort Parameter_No; public static ushort column; public static AGL4.NCK_Block block_type; public static AGL4.NCK_Area area; public static ushort row; public static byte rowCount; public static byte Unit; public static int Bufflen; public static byte Buff; public static AGL4.NCK_DDEVarFormat DDEVarType; //Methods that just describes the parameter for each PLC variable }
Deltalogic(PLC读取方法Scoped服务)
public class Deltalogic { [Inject] Parameter_840D Parameter_840D { get; set; } public Int32 doNCK_ReadMixEx(Int32 connnr, Int32 timeout, out string NCK_Data) { //Reset_Parameter_value(); NCK_Data = ""; Parameter_840D Parameter_840D = new Parameter_840D(); connnr = Return_Connect_NCK; Int32 result = 0; AGL4.NckDataRW[] rwfield = new AGL4.NckDataRW[1]; rwfield[0] = new AGL4.NckDataRW(); rwfield[0].Area = Parameter_840D.area; rwfield[0].Block = Parameter_840D.block_type; rwfield[0].Column = Parameter_840D.column; rwfield[0].Row = Parameter_840D.row; rwfield[0].RowCount = Parameter_840D.rowCount; rwfield[0].Unit = Parameter_840D.Unit; rwfield[0].DDEVarType = Parameter_840D.DDEVarType; rwfield[0].BuffLen = Parameter_840D.Bufflen; rwfield[0].Buff = new Byte[rwfield[0].BuffLen]; rwfield[0].Result = 0; result = AGL4.NCK_ReadMixEx(connnr, ref rwfield, timeout); return result; } }
问题根源分析
- 静态变量共享冲突:
Parameter_840D里的所有参数都是静态的,静态变量属于类本身而非实例。Blazor Server的Scoped服务虽然每个客户端有独立实例,但静态变量会被所有客户端共享。当两个客户端同时修改静态参数时,会互相覆盖,导致doNCK_ReadMixEx拿到错误配置去读PLC,出现乱码或数据错误。 - 错误的实例创建:
Deltalogic的doNCK_ReadMixEx方法里手动new了Parameter_840D实例,绕过了依赖注入的Scoped实例,同时因为参数是静态的,新实例依然使用共享变量,加剧冲突。 - 竞态条件:周期性读取的同步方法没有线程安全保护,多个客户端线程同时操作静态变量,引发数据混乱。
解决方案
方案1:重构参数传递,弃用静态变量(优先推荐)
如果第三方DLL并非真的强制要求静态变量(可能是之前的误解),把Parameter_840D改为非静态类,用实例成员存储参数:
public class Parameter_840D { public byte channel_no = 1; public ushort Parameter_No; public ushort column; public AGL4.NCK_Block block_type; public AGL4.NCK_Area area; public ushort row; public byte rowCount; public byte Unit; public int Bufflen; public byte Buff; public AGL4.NCK_DDEVarFormat DDEVarType; // 方法改为操作当前实例的参数 public void Par_840D_Channel_Status(byte channelNo) { this.channel_no = channelNo; // 配置当前实例的其他参数... } }
修改Deltalogic方法,接收Parameter_840D实例作为参数:
public class Deltalogic { public Int32 doNCK_ReadMixEx(Int32 connnr, Int32 timeout, Parameter_840D param, out string NCK_Data) { NCK_Data = ""; connnr = Return_Connect_NCK; Int32 result = 0; AGL4.NckDataRW[] rwfield = new AGL4.NckDataRW[1]; rwfield[0] = new AGL4.NckDataRW(); rwfield[0].Area = param.area; rwfield[0].Block = param.block_type; rwfield[0].Column = param.column; rwfield[0].Row = param.row; rwfield[0].RowCount = param.rowCount; rwfield[0].Unit = param.Unit; rwfield[0].DDEVarType = param.DDEVarType; rwfield[0].BuffLen = param.Bufflen; rwfield[0].Buff = new Byte[rwfield[0].BuffLen]; rwfield[0].Result = 0; result = AGL4.NCK_ReadMixEx(connnr, ref rwfield, timeout); // 处理NCK_Data赋值逻辑 return result; } }
Razor.cs调用修改为:
public void Read_PLC_Data() { Parameter_840D.Par_840D_Channel_Status(Deltalogic.channel_no); Deltalogic.doNCK_ReadMixEx(Deltalogic.MAE_Status_DevNr, 5000, Parameter_840D, out Deltalogic.MAE_Status_Mode); // 其他参数读取同理 }
方案2:强制静态变量场景下加线程锁
如果第三方DLL确实要求静态变量,必须在访问静态参数时加线程锁,确保同一时间只有一个客户端操作静态变量:
在Parameter_840D添加静态锁对象:
public class Parameter_840D { private static readonly object _lockObj = new object(); // 保留原有静态参数... public void Par_840D_Channel_Status(byte channelNo) { lock(_lockObj) { channel_no = channelNo; // 配置其他静态参数... } } }
修改Deltalogic的方法,加锁执行读取:
public Int32 doNCK_ReadMixEx(Int32 connnr, Int32 timeout, out string NCK_Data) { lock(Parameter_840D._lockObj) { NCK_Data = ""; connnr = Return_Connect_NCK; Int32 result = 0; AGL4.NckDataRW[] rwfield = new AGL4.NckDataRW[1]; rwfield[0] = new AGL4.NckDataRW(); rwfield[0].Area = Parameter_840D.area; // 其他参数赋值... result = AGL4.NCK_ReadMixEx(connnr, ref rwfield, timeout); return result; } }
注意:此方案会让客户端读取操作串行化,性能受影响,适合客户端数量较少的场景。
额外优化
- 用
Timer替代for循环+Task.Delay实现周期性读取,避免占用组件线程。 - 确保每个客户端的PLC连接(
Return_Connect_NCK对应的连接)是独立实例,不要共享连接。
内容的提问来源于stack exchange,提问作者Mdarende
相关产品推荐
相关产品推荐

