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

C#开发Discord Bot遇到线程同步问题求助

解决方案:线程安全字典 + 可靠定时任务

针对多线程操作字典导致数据异常、同时担心lock影响定时更新的问题,给出以下具体解决步骤:

1. 替换为线程安全字典

普通Dictionary<TKey,TValue>不支持多线程并发操作,直接改用ConcurrentDictionary<TKey,TValue>——它内部已实现线程安全的增删改查逻辑,无需手动加锁,能从根源避免数据竞争问题。

首先修改字典定义:

// 替换原来的普通字典
private readonly ConcurrentDictionary<string, string> queue = new ConcurrentDictionary<string, string>();
private readonly ConcurrentDictionary<string, string> blackList = new ConcurrentDictionary<string, string>();

然后修改所有字典操作代码,使用ConcurrentDictionary的安全方法:

// UpdateQueue中的添加操作
queue.TryAdd("test", "test");
blackList.TryAdd("test", "test");

// Discord_ButtonIsPressed中的黑名单添加
blackList.TryAdd("test", args.User.Id.ToString());

读取字典值时推荐用TryGetValue,避免键不存在引发的异常:

if (queue.TryGetValue("test", out var userContent))
{
    embed.AddField("firstUser", userContent);
}
else
{
    embed.AddField("firstUser", "队列暂无用户");
}

2. 优化定时任务,保证更新间隔

当前用Thread+while(true)的定时方式不够可靠,改用.NET 6+提供的PeriodicTimer,它会确保每次任务完成后再等待30秒,不会因任务执行耗时导致间隔混乱,同时避免不必要的线程浪费。

修改按钮点击事件中的定时逻辑:

private async void button10_Click(object sender, EventArgs e)
{
    // 初始化30秒间隔的定时器
    using var updateTimer = new PeriodicTimer(TimeSpan.FromSeconds(30));
    
    // 启动后台定时任务
    _ = Task.Run(async () =>
    {
        while (await updateTimer.WaitForNextTickAsync())
        {
            await UpdateQueue();
        }
    });

    channel = await discord.GetChannelAsync(queueChannelID);
    discord.ComponentInteractionCreated += Discord_ButtonIsPressed;
    await discord.ConnectAsync();
    await Task.Delay(-1);
}

3. 若必须用lock,缩小锁定范围

如果因特殊需求必须使用普通字典+lock,绝对不要把异步操作(比如await message.ModifyAsync)放在lock块内,否则会导致lock持有时间过长,影响定时精度。仅在操作字典的代码段加锁,且每个字典使用单独的锁对象:

// 定义单独的锁对象
private readonly object queueLock = new object();
private readonly object blackListLock = new object();

private async Task UpdateQueue()
{
    // 仅锁定字典操作的代码
    lock (queueLock)
    {
        queue.Add("test", "test");
    }
    
    lock (blackListLock)
    {
        blackList.Add("test", "test");
    }

    // 异步操作放在lock外,不占用锁资源
    DiscordMessage message = await channel.GetMessageAsync(messageID);
    await message.ModifyAsync(queue["test"]);
}

额外提示

  • 避免async void方法中出现未处理的异常,UI事件的async void虽允许,但建议内部添加try-catch块。
  • 从数据库读取队列/黑名单数据时,建议一次性读取后批量更新到线程安全字典,减少并发操作次数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:45:12