C#中MySqlDataReader与Timer同步异常问题排查求助
轮询MySQL视图输出不同步问题排查与修复
问题概述
使用Timer组件轮询MySQL数据库视图,几轮后输出出现不同步现象,且该问题与轮询时长无关。以下是相关代码及输出日志,结合内容排查问题原因。
代码核心问题分析
从提供的代码来看,输出不同步的根源在于以下几点:
- Timer重复实例化与事件重复绑定
每次调用startPoll都会创建新的Timer实例,并且重复绑定Elapsed事件。这会导致多个Timer同时运行,多个查询任务并行执行,输出结果交叉混乱,对应日志里重复的Timer Started和交错的查询输出。 - UI线程操作不安全
在Timer的Elapsed事件逻辑中直接修改stateText的文本和颜色,而Elapsed事件运行在非UI线程,直接操作UI控件会引发线程安全问题,可能导致隐性的同步异常。 - 数据库资源冗余释放
代码已用using语句包裹MySqlConnection、MySqlCommand和MySqlDataReader,这些对象会在using块结束时自动释放资源,手动调用rdr.Close()和conn.Close()属于冗余操作,甚至可能引发资源释放顺序错误。 - 无增量查询逻辑
每次查询都是SELECT * from vtokenpool limit 10,没有标记已处理的记录,导致重复输出相同数据,加上多线程并行执行,进一步加剧了输出混乱。
修复后的代码示例
// 全局仅初始化一次Timer private Timer _pollingTimer; private void StartPolling() { // 避免重复创建Timer实例 if (_pollingTimer == null) { int interval = int.Parse(Properties.Settings.Default.DbPoll) * 1000; _pollingTimer = new Timer(interval); _pollingTimer.Elapsed += HandlePollingEvent; } _pollingTimer.Stop(); _pollingTimer.Start(); System.Diagnostics.Debug.WriteLine("Timer Started " + Properties.Settings.Default.DbPoll + " secs"); } private void HandlePollingEvent(object sender, ElapsedEventArgs e) { // 查询前暂停Timer,避免并行执行 _pollingTimer.Stop(); 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"); // 记录上次处理的最大ID,实现增量查询(假设视图有自增ID列) static long _lastProcessedId = 0; var stm = $"SELECT * from vtokenpool WHERE id > {_lastProcessedId} ORDER BY id LIMIT 10"; using (var cmd = new MySqlCommand(stm, conn)) { // 切换到UI线程更新控件状态 stateText.Invoke(new Action(() => { stateText.Text = "Running"; stateText.ForeColor = System.Drawing.Color.Green; })); using (MySqlDataReader rdr = cmd.ExecuteReader()) { while (rdr.Read()) { long currentId = Convert.ToInt64(rdr[0]); System.Diagnostics.Debug.WriteLine(currentId + " -- " + rdr[1]); // 更新最后处理的ID,避免重复输出 if (currentId > _lastProcessedId) { _lastProcessedId = currentId; } } System.Diagnostics.Debug.WriteLine("Reader Processing Completed"); } } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine("Polling Error: " + ex); // 切换到UI线程更新错误状态 stateText.Invoke(new Action(() => { stateText.Text = "Error"; stateText.ForeColor = System.Drawing.Color.Red; })); } finally { // 重启Timer,保证下一轮轮询正常执行 _pollingTimer.Start(); System.Diagnostics.Debug.WriteLine("Timer Restarted"); } }
日志异常说明
从提供的输出日志可见:
- 重复出现
Timer Started、Timer Ended,说明存在多个Timer实例同时运行。 - 查询结果交错输出(如59号记录多次出现),是因为多个并行的查询任务同时输出结果,导致顺序混乱。
修复后通过单Timer实例、查询前暂停Timer、增量查询逻辑,即可解决输出不同步的问题。
内容的提问来源于stack exchange,提问作者Sudomasochist
相关产品推荐
相关产品推荐

