.NET Core 3.1 Worker Service中OPC DA客户端DataChange事件无法触发的问题求助
解决.NET Core 3.1 Worker Service中OPC DA DataChange事件不触发的问题
我之前遇到过完全相同的场景,问题主要出在Worker Service的生命周期管理和COM组件的线程模型适配上,下面是针对性的解决方案:
1. 修复Worker Service的运行生命周期
你的ExecuteAsync方法只执行了一次Task.Delay(1000)就结束了,而BackgroundService的设计逻辑是:当ExecuteAsync返回时,服务会判定工作完成并停止运行,后续的OPC事件自然没有机会触发。
修改ExecuteAsync,让它持续运行直到收到停止信号:
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // 保持服务持续运行,直到收到停止指令 while (!stoppingToken.IsCancellationRequested) { await Task.Delay(1000, stoppingToken).ConfigureAwait(false); } }
或者更简洁的写法:
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // 无限等待,直到停止信号触发 await Task.Delay(Timeout.Infinite, stoppingToken).ConfigureAwait(false); }
2. 调整事件注册与项添加的顺序
你当前是先添加订阅项再注册DataChanged事件,这可能会错过初始的数据变更通知。正确的顺序应该是先注册事件,再添加订阅项:
public override Task StartAsync(CancellationToken cancellationToken) { url = new Opc.URL("opcda://localhost/OPC Server"); server = new Opc.Da.Server(fact, null); server.Connect(url, new Opc.ConnectData(new System.Net.NetworkCredential())); groupState = new Opc.Da.SubscriptionState(); groupState.Name = "Group"; groupState.UpdateRate = 1000; groupState.Active = true; groupRead = (Opc.Da.Subscription)server.CreateSubscription(groupState); // 先注册事件,再添加Items groupRead.DataChanged += GroupRead_DataChanged; itemsList.Clear(); foreach (var tag in listTagNamesFrmconfigFile) { var item = new Item(); item.ItemName = $"{opcTopicName}{tag.Name}"; itemsList.Add(item); } groupRead.AddItems(itemsList.ToArray()); return base.StartAsync(cancellationToken); }
3. 适配COM组件的STA线程模型
OPC DA基于COM实现,多数OPC服务器要求事件处理在**STA(单线程单元)**线程中执行,而.NET Core默认的后台线程是MTA(多线程单元),这会导致事件无法正确触发。
将OPC的初始化逻辑放到STA线程中执行:
public override Task StartAsync(CancellationToken cancellationToken) { // 使用STA线程初始化OPC连接和订阅 var staTask = Task.Factory.StartNew(() => { url = new Opc.URL("opcda://localhost/OPC Server"); server = new Opc.Da.Server(fact, null); server.Connect(url, new Opc.ConnectData(new System.Net.NetworkCredential())); groupState = new Opc.Da.SubscriptionState(); groupState.Name = "Group"; groupState.UpdateRate = 1000; groupState.Active = true; groupRead = (Opc.Da.Subscription)server.CreateSubscription(groupState); groupRead.DataChanged += GroupRead_DataChanged; itemsList.Clear(); foreach (var tag in listTagNamesFrmconfigFile) { var item = new Item(); item.ItemName = $"{opcTopicName}{tag.Name}"; itemsList.Add(item); } groupRead.AddItems(itemsList.ToArray()); }, cancellationToken, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); staTask.Wait(cancellationToken); return base.StartAsync(cancellationToken); }
4. 正确释放资源(可选但关键)
在服务停止时,记得清理OPC连接和订阅,避免资源泄漏:
public override async Task StopAsync(CancellationToken cancellationToken) { if (groupRead != null) { server.CancelSubscription(groupRead); groupRead.Dispose(); groupRead = null; } if (server != null && server.IsConnected) { server.Disconnect(); server.Dispose(); server = null; } await base.StopAsync(cancellationToken); }
为什么控制台应用能正常运行?
控制台应用的主线程默认是STA(.NET Framework环境下),并且会持续运行直到用户主动关闭,所以OPC事件能正常触发。而Worker Service的后台线程是MTA,且如果ExecuteAsync提前返回,整个服务会停止,导致事件无法被处理。
内容的提问来源于stack exchange,提问作者PRH
相关产品推荐
相关产品推荐

