C# 实现双方法每毫秒后台并行运行不阻塞主流程问题咨询
问题核心原因
- 你当前用的是递归自调用实现循环,会持续占用当前线程执行,完全不会让出CPU资源给其他业务逻辑,而且还会不断累加调用栈,时间长了还会栈溢出崩溃
- 所有数据库操作都是用的同步方法(
ExecuteNonQuery、ExecuteReader),没有用对应的异步版本,即使方法标注了async Task也没有实际异步执行,会阻塞线程 - 没有加任何时间间隔控制,两个方法会无间隔疯狂执行,不仅占满资源,还会给数据库造成极大压力,你要的1ms间隔也根本没实现
- 调用
MuteRead()和MuteUpdate()的时候没有加await,方法标注的async完全没起到作用,会直接同步阻塞执行
修复方案
第一步:改写两个方法为正确的异步实现
把同步数据库调用全部改成异步版本,加上await,同时用using自动释放数据库连接,不用手动关闭:
// 公共的连接字符串可以提出来,不用每个方法都写一遍 private const string DbConnStr = "Server=echstreme.de;Port=3306;Database=c1Look;Uid=c1Look;Pwd=Redacted;SSL Mode =None"; public async Task MuteUpdateAsync() { try { using var conn = new MySqlConnection(DbConnStr); await conn.OpenAsync(); using var cmd = new MySqlCommand(SQLMUteRemove, conn); await cmd.ExecuteNonQueryAsync(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } } public async Task MuteReadAsync() { try { using var conn = new MySqlConnection(DbConnStr); await conn.OpenAsync(); using var cmd = new MySqlCommand("SELECT * FROM `mutes` WHERE MuteUntil <= 0", conn); using var rdr = await cmd.ExecuteReaderAsync(); while (await rdr.ReadAsync()) { ulong id = Convert.ToUInt64(rdr[1]); var user = Context.Guild.GetUser(id); await SendLog("__Mute Manager__", $"**Member** | <@{rdr[1]}>\n **Punishment** | UNMUTE\n **Punished by** | <@{rdr[4]}>"); // 这里DELETE操作使用参数化查询,避免SQL注入 using var deleteCmd = new MySqlCommand("DELETE FROM `mutes` WHERE `UserID` = @UserId", conn); deleteCmd.Parameters.AddWithValue("@UserId", rdr[1]); await deleteCmd.ExecuteNonQueryAsync(); await user.RemoveRoleAsync(876561161062076416); } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } }
第二步:用后台定时任务代替递归实现间隔执行
用异步循环加延迟的方式,不会阻塞主线程,其他逻辑可以正常执行:
// 程序启动的时候调用一次这个方法启动后台任务,不要await它,让它在后台独立运行 _ = RunMuteJobsAsync(); private async Task RunMuteJobsAsync() { // 可通过修改该变量控制任务启停 while (true) { // 执行两个业务方法 await MuteUpdateAsync(); await MuteReadAsync(); // 等待1ms再执行下一轮 await Task.Delay(1); } }
注:Windows系统默认定时器精度为10~15ms,即使设置
Task.Delay(1),实际间隔也不会低于10ms,对于静音管理这类场景完全不需要毫秒级精度,建议把间隔调整到1秒以上,大幅降低数据库压力。
额外优化建议
- 你原来的UPDATE逻辑是每次把MuteUntil减1,完全没必要高频跑SQL更新,可以直接在查询的时候判断
MuteUntil <= UNIX_TIMESTAMP()(如果存的是Unix时间戳),直接去掉UPDATE步骤,性能会提升很多 - 所有SQL操作都要坚持使用参数化查询,不要直接拼接用户输入的内容,避免SQL注入风险
内容的提问来源于stack exchange,提问作者Luke
相关产品推荐
相关产品推荐

