为什么单实例Discord.Net程序有时会重复执行同一段代码?
根因定位
你的问题核心是Discord库的重连机制触发了重复的Ready事件,而你将游戏调度的初始化逻辑放在了Ready事件回调中,每次重连都会新增一个独立的等待任务,多个任务同时倒计时结束后就会重复调用StartNewGame。闲置时网络波动触发重连的概率更高,符合你观察到的闲置后复现、重启后暂时消失的特征。
排查验证步骤
- 在
Ready事件入口加日志,输出触发时间和线程ID,确认每次重连时是不是多次触发了该事件 - 检查调度逻辑的取消机制:原有逻辑里的
Task.Delay没有绑定取消令牌,重连后旧的等待任务不会被销毁,会一直运行到倒计时结束 - 确认你使用的是全局共享的MySql连接对象,多线程同时操作时自然会触发DataReader占用的崩溃问题
解决方案
1. 调度逻辑加去重取消机制
全局定义取消令牌源,每次触发Ready事件时先销毁旧的等待任务,再启动新的调度,避免多任务残留:
// 全局定义 private static CancellationTokenSource _gameScheduleCts = new CancellationTokenSource(); // Ready事件回调中的调度逻辑替换为以下代码 // 先取消旧的调度任务 _gameScheduleCts.Cancel(); _gameScheduleCts = new CancellationTokenSource(); var currentToken = _gameScheduleCts.Token; DateTime deadline = Convert.ToDateTime(dt.Rows[0][0]); int ms = (int)(deadline - DateTime.Now).TotalMilliseconds; if (ms > 0) { Console.WriteLine("Waiting until " + deadline.ToLongTimeString() + " to start a new game."); try { await Task.Delay(ms, currentToken); } catch (TaskCanceledException) { // 任务被取消直接退出,不执行后续逻辑 return; } } // 执行前再判断一次令牌状态,避免极端情况的并发 if (!currentToken.IsCancellationRequested) { await StartNewGame("All Any", false, false); }
2. 修正数据库连接使用方式
不要使用全局共享的MySql连接,ADO.NET自带连接池,每次操作单独开连接即可,完全避免多线程访问冲突,还能解决DataReader崩溃的问题:
// 将StartNewGame中的数据库操作替换为以下写法 cmdText = string.Format("update servers set deadline = '{0}' where serverid = '{1}'", newtry.ToString("yyyy-MM-dd HH:mm:ss"), _bot.server.Id); using (var conn = new MySqlConnection(你的数据库连接字符串)) { await conn.OpenAsync(); using (var cmd = new MySqlCommand(cmdText, conn)) { await cmd.ExecuteNonQueryAsync(); } }
3. 加最终兜底互斥逻辑
就算前面的逻辑出问题,也保证StartNewGame同一时间只能运行一个实例:
// 全局定义锁对象 private static readonly SemaphoreSlim _startGameSemaphore = new SemaphoreSlim(1, 1); public async Task StartNewGame(string type, bool forced, bool speed, List<ulong> players = null) { // 尝试获取锁,获取失败直接返回,避免重复执行 if (!await _startGameSemaphore.WaitAsync(0)) { return; } try { // 原有方法逻辑 } finally { _startGameSemaphore.Release(); } }
4. 优化Discord库重连配置
如果你使用的是Discord.NET库,检查以下配置减少不必要的重连:
- 开启所有需要的GatewayIntents,避免因为权限不足被服务端断开连接
- 配置
AlwaysDownloadUsers = true减少首次连接时的超时概率 - 调整连接超时参数,避免弱网环境下频繁重连
内容的提问来源于stack exchange,提问作者Matthijn Zwart
相关产品推荐
相关产品推荐

