Roke_123 2026-02-03 09:53 采纳率: 66.7%
浏览 7
已采纳

OPC SERVER运行一段时间后异常退出

OPC SERVER运行一段时间后异常退出,求指教

img

我用.NET开发了一个OPC SERVER,开启后可以正常运行,但是运行一段时间后莫名弹出这个窗口后,程序停止了。在程序中各阶段捕获异常都没能捕获到。

主要更新数据的逻辑如下:

 ThreadPool.SetMinThreads(4, 4);  // 工作线程和I/O线程的最小值
            ThreadPool.SetMaxThreads(20, 20); // 工作线程和I/O线程的最大值
            List<string> tag_list = new List<string>();
            int count = 0;
            try
            {
                foreach (string tag in tags)
                {
                    tag_list.Add(tag);
                    count++;
                    if (tag_list.Count == 10 || count == tags.Count)
                    {
                        ThreadPool.QueueUserWorkItem(new WaitCallback(this.UpdateValue), new List<string>(tag_list));
                        tag_list.Clear();
                    }
                }
            }catch(Exception ex)
            {
                TxtLog.WriteTextLog("程序异常:" + ex.Message + ex.StackTrace, DateTime.Now, "主任务异常");

            }

 private void UpdateValue(object obj)
        {
            try
            {
                List<string> tags = obj as List<string>;
                string tagName = string.Empty;
                string sqlPiStr = string.Empty;
                PiData pid = new PiData();
                DataTable piDt = new DataTable();
                double value = 0;
                int itemHandle = 0;
                string dataType = string.Empty;
                string tagNames = "";//常位号
                string kgTagNames = "";//开关位号
                foreach (string tag in tags)
                {
                    if (tag.Split('$').Length != 2)
                    {
                        TxtLog.WriteTextLog(tag + "位号设置异常", System.DateTime.Now, "位号设置异常");
                        continue;
                    }
                    dataType = tag.Split('$')[0];
                    tagName = tag.Split('$')[1];
                    if (dataType.ToUpper() == "DOUBLE" || dataType.ToUpper() == "FLOAT" || dataType.ToUpper() == "INT"
                        || dataType.ToUpper() == "SHORT" || dataType.ToUpper() == "USHORT"||dataType.ToUpper() == "BYTE")
                    {
                        //数值位号
                        tagNames += "'" + tagName + "',";
                       
                    } 
                    else
                    {
                        //bool类型待定
                        continue;
                    }
                   
                }
                if (tagNames.Length > 0)
                {
                    tagNames = tagNames.Remove(tagNames.Length - 1, 1);
                    sqlPiStr = string.Format(@"select tag,value,time   from [piarchive]..[picomp] where " +
                    " tag in ({0}) and time ='*'", tagNames);
                    try
                    {
                        piDt.Clear();
                        piDt = pid.rdtable(sqlPiStr);
                    }
                    catch (Exception ex)
                    {
                        TxtLog.WriteTextLog("数据读取失败," + ex.Message + ex.StackTrace + "\r\nsql=" + sqlPiStr, System.DateTime.Now, "实时数据采集异常");
                        return;
                    }
                    for (int i = 0; i < piDt.Rows.Count; i++)
                    {
                        if (double.TryParse(piDt.Rows[i]["value"].ToString(), out value))
                        {
                            tagName = piDt.Rows[i]["tag"].ToString();
                            //获取句柄
                            if (tagName_ItemHandle.TryGetValue(tagName, out itemHandle))
                            {
                                try
                                {
                                    ServerOpc.UpdateOPCItem(itemHandle, value);

                                }
                                catch (Exception ex)
                                {
                                    TxtLog.WriteTextLog(tagName + "-" + value + "-数据更新异常,erro=" + ex.Message + "\n" + ex.StackTrace, System.DateTime.Now, "更新数据异常");
                                }
                            }

                        }
                        else if (piDt.Rows[i]["value"] != null && piDt.Rows[i]["value"].ToString().Trim() != "")
                        {
                            tagName = piDt.Rows[i]["tag"].ToString();
                            //获取句柄
                            if (tagName_ItemHandle.TryGetValue(tagName, out itemHandle))
                            {
                                try
                                {
                                    ServerOpc.UpdateOPCItem(itemHandle, piDt.Rows[i]["value"].ToString().Trim());

                                }
                                catch (Exception ex)
                                {
                                    TxtLog.WriteTextLog(tagName + "-" + value + "-数据更新异常,erro=" + ex.Message + "\n" + ex.StackTrace, System.DateTime.Now, "更新数据异常");
                                }
                            }
                        }
                        else
                        {
                            TxtLog.WriteTextLog("更新数据失败,没有取到值(" + piDt.Rows[i]["value"].ToString() + "),sql=" + sqlPiStr + "\r\n", System.DateTime.Now, "更新数据失败");

                        }
                        //break;
                    }
                }
                if (kgTagNames.Length > 0)
                {
                    kgTagNames = kgTagNames.Remove(kgTagNames.Length - 1, 1);
                    sqlPiStr = string.Format(@"SELECT tag, time,DIGSTRING(status) value
                            FROM piarchive..picomp WHERE tag in ({0}) AND time = '*'", kgTagNames);
                    try
                    {
                        piDt.Clear();
                        piDt = pid.rdtable(sqlPiStr);
                    }
                    catch (Exception ex)
                    {
                        TxtLog.WriteTextLog("实时数据读取失败," + ex.Message + ex.StackTrace + "\r\nsql=" + sqlPiStr, System.DateTime.Now, "实时数据采集异常");
                        return;
                    }
                    for (int i = 0; i < piDt.Rows.Count; i++)
                    {
                        if (piDt.Rows[i]["value"].ToString().Trim().ToUpper() == "OFF")
                        {
                            tagName = piDt.Rows[i]["tag"].ToString();
                            //获取句柄
                            if (tagName_ItemHandle.TryGetValue(tagName, out itemHandle))
                            {
                                try
                                {
                                    ServerOpc.UpdateOPCItem(itemHandle, 0);

                                }
                                catch (Exception ex)
                                {
                                    TxtLog.WriteTextLog(tagName + "-" + piDt.Rows[i]["value"].ToString() + "-数据更新异常,erro=" + ex.Message + "\n" + ex.StackTrace, System.DateTime.Now, "更新数据异常");
                                }
                            }

                        }
                        else if (piDt.Rows[i]["value"].ToString().Trim().ToUpper() == "ON")
                        {
                            tagName = piDt.Rows[i]["tag"].ToString();
                            //获取句柄
                            if (tagName_ItemHandle.TryGetValue(tagName, out itemHandle))
                            {
                                try
                                {
                                    ServerOpc.UpdateOPCItem(itemHandle, 1);

                                }
                                catch (Exception ex)
                                {
                                    TxtLog.WriteTextLog(tagName + "-" + piDt.Rows[i]["value"].ToString() + "-数据更新异常,erro=" + ex.Message + "\n" + ex.StackTrace, System.DateTime.Now, "更新数据异常");
                                }
                            }
                        }
                        else
                        {
                            ///更新数据失败,没有取到值
                            //TxtLog.WriteTextLog("更新数据失败,没有取到值(" + piDt.Rows[i]["value"].ToString() + "),sql=" + sqlPiStr + "\r\n", System.DateTime.Now, "更新开关量数据失败");

                        }
                       
                    }
                }
            }
            catch(Exception ex)
            {
                               TxtLog.WriteTextLog("程序异常:" + ex.Message + ex.StackTrace, DateTime.Now, "更新任务异常");

            }
            
             
        }

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2026-02-03 09:54
    关注

    上午好☀️☀️☀️️
    本答案参考通义千问

    OPC Server 在运行一段时间后异常退出,可能由多种原因导致。根据你提供的代码和描述,以下是一些关键问题点及对应的解决方案


    一、问题分析

    1. 线程池资源耗尽

    你使用了 ThreadPool.QueueUserWorkItem 来异步执行数据更新任务,但没有对线程池的使用进行限制或监控。如果任务数量过多,可能导致线程池中所有线程被占用,最终引发线程阻塞或异常。

    2. 未处理的异常(未捕获)

    虽然你在主方法中尝试捕获异常,但某些异常可能发生在子线程中,例如在 UpdateValue 方法中发生的异常,没有被主程序捕获,导致整个程序崩溃。

    3. 内存泄漏或资源未释放

    你多次创建 DataTablePiData 对象,若未正确释放,可能会造成内存泄漏,最终导致程序崩溃。

    4. OPC Server 的稳定性问题

    如果 ServerOpc.UpdateOPCItem 方法本身存在不稳定因素(如不支持并发调用、超时等),也可能导致程序异常退出。


    二、解决方案

    1. 增加线程池的监控与限制

    建议: 避免过度使用线程池,可以使用 SemaphoreSlim 控制并发数,防止线程池资源耗尽。

    private SemaphoreSlim _semaphore = new SemaphoreSlim(10); // 控制最多同时执行10个任务
    
    // 修改你的 QueueUserWorkItem 调用如下:
    await _semaphore.WaitAsync();
    try
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(this.UpdateValue), new List<string>(tag_list));
    }
    finally
    {
        _semaphore.Release();
    }
    

    重点说明: 使用 SemaphoreSlim 可以控制并发数,避免线程池被耗尽。


    2. 确保所有线程中的异常都被捕获

    你当前只在主线程中捕获异常,而子线程中的异常没有被处理,会导致程序崩溃。

    修改方式:UpdateValue 方法中添加更全面的异常捕获,并记录日志。

    private void UpdateValue(object obj)
    {
        try
        {
            // 原有逻辑
        }
        catch (Exception ex)
        {
            TxtLog.WriteTextLog("子线程异常:" + ex.Message + "\n" + ex.StackTrace, DateTime.Now, "子线程异常");
        }
    }
    

    重点说明: 所有异步操作都应包含完整的异常捕获机制。


    3. 优化资源释放

    你频繁地创建 DataTablePiData 实例,建议在使用后及时释放资源,避免内存泄漏。

    修改建议:

    private void UpdateValue(object obj)
    {
        try
        {
            List<string> tags = obj as List<string>;
            DataTable piDt = null;
    
            // ... 其他逻辑 ...
    
            if (tagNames.Length > 0)
            {
                sqlPiStr = string.Format(@"select tag,value,time from [piarchive]..[picomp] where tag in ({0}) and time='*'", tagNames);
                try
                {
                    piDt = pid.rdtable(sqlPiStr);
                    // 处理 piDt 数据...
                }
                catch (Exception ex)
                {
                    TxtLog.WriteTextLog("数据读取失败:" + ex.Message + "\n" + ex.StackTrace, DateTime.Now, "实时数据采集异常");
                }
                finally
                {
                    if (piDt != null)
                    {
                        piDt.Dispose(); // 如果 DataTable 支持 Dispose,则释放
                    }
                }
            }
        }
        catch (Exception ex)
        {
            TxtLog.WriteTextLog("子线程异常:" + ex.Message + "\n" + ex.StackTrace, DateTime.Now, "子线程异常");
        }
    }
    

    重点说明: 确保所有资源对象(如 DataTableSqlConnectionSqlCommand 等)在使用后正确释放。


    4. 检查 OPC Server 的稳定性

    • 确认 ServerOpc.UpdateOPCItem(itemHandle, value) 是否支持多线程调用?
    • 是否有可能因为时间戳错误或句柄无效而导致异常?
    • 是否有超时设置?

    建议: 在调用 ServerOpc.UpdateOPCItem 时加入超时机制,避免长时间等待。

    try
    {
        Task.Run(() => ServerOpc.UpdateOPCItem(itemHandle, value)).Wait(5000); // 设置最大等待时间
    }
    catch (Exception ex)
    {
        TxtLog.WriteTextLog("OPC 更新超时或失败:" + ex.Message + "\n" + ex.StackTrace, DateTime.Now, "OPC 更新异常");
    }
    

    5. 日志详细记录

    确保所有关键操作都有详细的日志记录,包括:

    • 每次从数据库读取的数据
    • 每次更新 OPC 的值
    • 异常发生的时间、内容、堆栈信息

    重点说明: 日志是排查“无提示”异常的关键工具。


    三、推荐修改后的代码结构

    ThreadPool.SetMinThreads(4, 4);
    ThreadPool.SetMaxThreads(20, 20);
    
    List<string> tag_list = new List<string>();
    int count = 0;
    try
    {
        foreach (string tag in tags)
        {
            tag_list.Add(tag);
            count++;
            if (tag_list.Count == 10 || count == tags.Count)
            {
                await _semaphore.WaitAsync();
                try
                {
                    ThreadPool.QueueUserWorkItem(new WaitCallback(this.UpdateValue), new List<string>(tag_list));
                }
                finally
                {
                    _semaphore.Release();
                }
                tag_list.Clear();
            }
        }
    }
    catch (Exception ex)
    {
        TxtLog.WriteTextLog("主任务异常:" + ex.Message + "\n" + ex.StackTrace, DateTime.Now, "主任务异常");
    }
    

    四、总结

    | 问题 | 解决方案 | |------|----------| | 线程池资源耗尽 | 使用 SemaphoreSlim 控制并发 | | 子线程异常未捕获 | 在 UpdateValue 中添加完整异常捕获 | | 内存泄漏 | 释放 DataTable、PiData 等资源 | | OPC Server 不稳定 | 检查 UpdateOPCItem 的并发支持和超时机制 | | 日志缺失 | 添加详细日志记录 |


    如果你能提供更多的日志信息或错误截图,我可以进一步帮你定位具体原因。希望以上建议对你有所帮助!

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 已采纳回答 2月4日
  • 创建了问题 2月3日