C#多线程场景下EventHandler Invoke调用偶发不触发问题
C# 多HMIMaster实例事件偶发不触发问题修复
问题根因
代码存在3处明确的编写错误,直接导致事件偶发不触发:
- 事件绑定时机存在竞态条件:实例创建后先调用
Connect()、SetRegisters()方法(这两个方法大概率会直接启动后台轮询线程执行ReadReg逻辑),之后才绑定OnEvent事件处理方法。通信启动后到事件绑定完成前的时间窗口内,所有触发的ReadReg调用拿到的事件委托都是未绑定处理方法的null,自然不会执行HMIEvent中的逻辑,这就是ReadReg内日志能正常打印、事件处理日志缺失的核心原因。 - 事件委托拷贝时机错误:你在
ReadReg方法刚进入、还未执行读寄存器操作时就提前把OnEvent赋值给了handler局部变量,哪怕后续读操作耗时期间你绑定了事件,handler里存的还是之前的null,调用时不会触发任何处理方法。 - 集合线程安全问题:多线程场景下直接用非线程安全的
List<T>做_masters的Add、取Last值操作,可能出现事件绑定到错误实例、绑定操作丢失的问题。
修正方案
- 调整实例初始化顺序:先创建HMIMaster实例、绑定事件,再执行连接、寄存器配置等会启动后台通信的操作,从根源上堵上事件绑定的时间窗口。
- 把事件委托的局部拷贝逻辑移动到实际
Invoke调用前,保证拿到最新绑定的委托列表。 - 多线程访问
_masters集合时加锁,或者直接替换为ConcurrentBag<HMIMaster>这类线程安全集合,避免集合操作竞态。
修正后代码
HMIMaster类ReadReg方法修正
// 自定义委托重命名避免和系统自带EventHandler重名混淆 public delegate void HMIEventHandler(HMIEventArgs e); public event HMIEventHandler OnEvent; private void ReadReg(RegisterMap register) { if (!_hmiConnected) return; var eventArgs = new HMIEventArgs(); try { byte slaveId = 1; ushort startAddress = register.Register; eventArgs.Event = Events.GotData; eventArgs.IPAddress = _hostname; eventArgs.Name = register.FriendlyName; ushort[] val = _master.ReadHoldingRegisters(slaveId, startAddress, 1); eventArgs.Data = val[0]; // 委托拷贝移到Invoke前,获取最新绑定的处理方法 var handler = OnEvent; handler?.Invoke(eventArgs); Debug.WriteLine("Got data from " + _hostname + ":" + register.Register + "(" + register.FriendlyName + ") : " + val[0]); } catch (Exception err) { Debug.WriteLine(err.ToString()); } }
实例创建与绑定逻辑修正
new Thread(() => { tasks.Add(Task.Factory.StartNew(() => { // 先实例化、先绑事件,再启动通信 var hmiInstance = new HMIMaster(); hmiInstance.OnEvent += HMIEvent; hmiInstance.Connect(ipAddress, port).SetRegisters(registers); // 加锁保护非线程安全集合的多线程操作 lock (_masters) { _masters.Add(hmiInstance); Debug.WriteLine(_masters.Count + " masters"); } })); }).Start(); private static void HMIEvent(HMIEventArgs e) { Debug.WriteLine(">> in logger (" + e.IPAddress + " " + e.Event + ") >> " + e.Name + " :: " + e.Data); var handler = OnEvent; handler?.Invoke(e); }
内容的提问来源于stack exchange,提问作者mrid
相关产品推荐
相关产品推荐

