You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 13:17:49