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

使用MySqlDataReader轮询数据库重复报错:存在未关闭的关联DataReader

问题分析与解决方法

核心问题

你的代码存在几个关键问题导致重复查询时出现DataReader冲突:

  • 类级别的conn变量被多线程(Timer的Elapsed事件)共享,前一次数据库操作未完全释放,后一次就复用连接,引发资源冲突。
  • 你把单次查询逻辑写在了startPoll方法里,但Timer的OnEventExecution才是轮询的执行入口,推测该事件里的数据库操作复用了同一个未正确隔离的连接对象。
  • 手动调用rdr.Close()和conn.Close()属于多余操作,using语句会自动释放资源,手动关闭反而可能导致资源释放时机混乱。

修复步骤

1. 完全隔离每次轮询的数据库资源

每次轮询都创建独立的连接、命令和DataReader,不要复用类级别的连接变量。把数据库查询逻辑封装到单独的方法中,确保每次调用都使用全新的局部资源:

private void OnEventExecution(object sender, ElapsedEventArgs e)
{
    // 跨线程更新UI需要Invoke
    if (stateText.InvokeRequired)
    {
        stateText.Invoke(new Action(() => 
        {
            stateText.Text = "Running";
            stateText.ForeColor = System.Drawing.Color.Green;
        }));
    }
    else
    {
        stateText.Text = "Running";
        stateText.ForeColor = System.Drawing.Color.Green;
    }

    string connectionString = $"server={Properties.Settings.Default.DbServer};database=digital_passports;uid={Properties.Settings.Default.DbUser};pwd={Properties.Settings.Default.DbPass}";
    
    try
    {
        using (var conn = new MySqlConnection(connectionString))
        {
            conn.Open();
            System.Diagnostics.Debug.WriteLine("Database Connection Connected");

            var stm = "SELECT * from vtokenpool limit 10";
            using (var cmd = new MySqlCommand(stm, conn))
            {
                using (var rdr = cmd.ExecuteReader())
                {
                    while (rdr.Read())
                    {
                        System.Diagnostics.Debug.WriteLine($"{rdr[0]} -- {rdr[1]}");
                    }
                }
            }
        } // using结束自动关闭conn和释放资源
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine(ex.ToString());
        // 出错时更新UI状态
        if (stateText.InvokeRequired)
        {
            stateText.Invoke(new Action(() => 
            {
                stateText.Text = "Error";
                stateText.ForeColor = System.Drawing.Color.Red;
            }));
        }
        else
        {
            stateText.Text = "Error";
            stateText.ForeColor = System.Drawing.Color.Red;
        }
    }
}

2. 修正startPoll方法的逻辑

startPoll只负责初始化和启动Timer,不要在里面执行单次查询:

private void startPoll()
{
    System.Diagnostics.Debug.WriteLine("dbForm created");
    int pollInterval = int.Parse(Properties.Settings.Default.DbPoll) * 1000;
    timer = new Timer(pollInterval);
    timer.Elapsed += OnEventExecution;
    timer.Start();
    System.Diagnostics.Debug.WriteLine($"Timer Started {Properties.Settings.Default.DbPoll} secs");
}

3. 移除多余的手动关闭操作

using语句会在代码块结束时自动调用对象的Dispose方法,自动关闭DataReader和Connection,无需手动调用rdr.Close()或conn.Close(),避免资源释放顺序混乱。

4. 确保Timer线程安全

Timer的Elapsed事件是在ThreadPool线程中触发的,直接操作UI控件会引发跨线程异常,必须使用Invoke来更新UI。

额外建议

  • 考虑使用async/await异步操作数据库,避免阻塞UI线程:将查询方法改为异步,在OnEventExecution中调用异步方法(注意Timer的异步处理需要用async void,但要做好异常捕获)。
  • 添加连接超时设置:在连接字符串中加入ConnectionTimeout=10,避免连接挂起占用资源。

内容的提问来源于stack exchange,提问作者Sudomasochist

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 15:52:50