You need to enable JavaScript to run this app. C#运行Discord Bot时如何在不阻塞其他任务的前提下监听控制台用户输入?
优惠活动
大模型
产品
解决方案
定价
更多

C#运行Discord Bot时如何在不阻塞其他任务的前提下监听控制台用户输入?

适配现有Discord Bot的非阻塞控制台输入实现

你遇到的阻塞问题核心是:同步调用的Console.ReadLine()会独占所在线程,直到用户完成输入才释放资源,直接卡住Bot的Discord消息事件循环。不需要引入复杂第三方组件,用.NET原生的异步任务隔离就能实现双端并行运行。

实现核心逻辑

  • 将控制台输入的监听逻辑完全隔离到独立后台任务中,和Discord客户端的主运行流程并行执行,互不抢占线程资源
  • 用异步控制台读取方法替代同步阻塞的Console.ReadLine(),其中使用Console.In.ReadLineAsync()替代传统同步方法是实现非阻塞运行的关键——这个方法在等待用户输入的过程中,会把对应线程释放到线程池处理其他任务,完全不会出现独占线程导致流程卡住的问题
  • 完全保留你原有代码里的Discord Bot功能逻辑,只补充输入监听的独立模块,改动量最小,适配C#初学者的理解难度

适配后可直接运行的代码

class Program
{
    public static void Main(string[] args)
        => new Program().MainAsync().GetAwaiter().GetResult();

    private DiscordSocketClient _client;
    // 用于实现Bot的优雅关闭
    private CancellationTokenSource _cts = new CancellationTokenSource();

    public async Task MainAsync()
    {
        _client = new DiscordSocketClient();
        _client.MessageReceived += CommandHandler;
        _client.Log += Log;

        var token = File.ReadAllText("token.txt");

        await _client.LoginAsync(TokenType.Bot, token);
        await _client.StartAsync();

        // 并行运行两个流程:后台监听控制台输入、维持Bot运行直到收到退出信号
        var consoleListenTask = Task.Run(async () => await ConsoleInputLoopAsync(_cts.Token));
        await Task.WhenAny(consoleListenTask, Task.Delay(Timeout.Infinite, _cts.Token));

        // 收到退出信号后安全关闭Bot连接
        await _client.StopAsync();
    }

    // 跑在独立后台线程的控制台输入循环
    private async Task ConsoleInputLoopAsync(CancellationToken ct)
    {
        // 循环监听输入,直到收到退出取消信号
        while (!ct.IsCancellationRequested)
        {
            // 异步读取控制台输入,等待过程不占用线程
            var userInput = await Console.In.ReadLineAsync();
            if (string.IsNullOrWhiteSpace(userInput)) continue;

            // 在这里自定义你需要的控制台指令逻辑
            userInput = userInput.Trim().ToLower();
            switch (userInput)
            {
                case "stop":
                    Console.WriteLine("收到停止指令,正在关闭Bot...");
                    _cts.Cancel();
                    break;
                case "status":
                    Console.WriteLine($"当前Bot连接状态:{_client.ConnectionState},已接入服务器数量:{_client.Guilds.Count}");
                    break;
                default:
                    Console.WriteLine($"未识别指令:{userInput},可用内置指令:stop、status");
                    break;
            }
        }
    }

    private Task Log(LogMessage msg)
    {
        Console.WriteLine(msg.ToString());
        return Task.CompletedTask;
    }

    public Task CommandHandler(SocketMessage message)
    {
        // 你原有的Discord消息处理逻辑完全无需修改
        string command = message.Content.Substring(0, message.Content.Length).ToLower();
        Random rnd = new Random();
        string[] greetings = { "hello", "hey", "hi", "sup" };
        string[] greetingResponse = 
        { 
            $"Hello {message.Author.Mention}", 
            $"Hey {message.Author.Mention}", 
            $"Hi {message.Author.Mention}" 
        };
        int greetingsIndex = rnd.Next(0, greetingResponse.Length);

        for (int i = 0; i < greetings.Length; i++)
        {
            if (command.Equals(greetings[i]) || command.Contains($"{greetings[i]} "))
            {
                _ = message.Channel.SendMessageAsync($@"{greetingResponse[greetingsIndex]}");
            }
        }
        return Task.CompletedTask;
    }
}

运行效果与注意事项

  • 改造后Bot的消息接收、日志输出、自动回复功能完全不受控制台输入等待的影响,两个流程并行运行,不会出现阻塞
  • 你可以随时在控制台输入自定义指令,所有操作都不会打断Bot的正常服务
  • 注意不要把控制台输入的逻辑直接写在主流程的启动阶段,否则还是会回到同步阻塞的状态,必须用独立后台任务隔离输入监听的循环
  • 现有Discord.Net的SocketClient本身是线程安全的,不需要额外写复杂的线程锁逻辑,跨线程调用状态读取、连接关闭的方法不会出现异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.06 01:24:03