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

C# Async/Await问题:如何让回调在循环等待时执行而非阻塞

解决回调被阻塞的问题:用异步等待替代同步循环

你的问题根源在于QuestionResponse里的同步阻塞循环——while (questions[question] == Response.None) Thread.Sleep(100);会一直占用当前线程,导致Discord的回调函数(SetResponse)无法及时执行,直到这个循环结束。要解决这个问题,我们需要用异步等待机制来替代同步阻塞,让线程能空闲出来处理回调。

重构方案:使用TaskCompletionSource实现异步等待

我们可以用TaskCompletionSource<T>来创建一个可等待的任务,当用户做出反应时,回调函数直接完成这个任务,而不是等待同步循环结束。

1. 重构QuestionUtils类

public static class QuestionUtils
{
    // 用TaskCompletionSource<bool>替代原来的Response枚举字典,每个问题对应一个可等待的任务
    private static readonly Dictionary<int, TaskCompletionSource<bool>> _questionTasks = new();
    private static int _nextQuestionId = 0;

    public static int GenerateQuestion()
    {
        int questionId = Interlocked.Increment(ref _nextQuestionId);
        // 为每个新问题创建一个TaskCompletionSource
        _questionTasks[questionId] = new TaskCompletionSource<bool>();
        return questionId;
    }

    public static void SetResponse(int questionId, bool isYes)
    {
        if (_questionTasks.TryGetValue(questionId, out var tcs))
        {
            // 完成任务,设置结果
            tcs.SetResult(isYes);
            // 清理已完成的任务,避免内存泄漏
            _questionTasks.Remove(questionId);
        }
    }

    // 改成异步方法,返回Task<bool>,不再阻塞线程
    public static async Task<bool> QuestionResponse(int questionId)
    {
        if (_questionTasks.TryGetValue(questionId, out var tcs))
        {
            // 异步等待任务完成,这会让出线程,让回调可以正常执行
            return await tcs.Task.ConfigureAwait(false);
        }
        throw new InvalidOperationException("Invalid question ID");
    }
}

2. 修改Instagram方法

因为QuestionResponse现在是异步方法,我们需要用await来等待结果,而不是同步调用:

public async Task Instagram(string username, int timeout = 10)
{
    int question = QuestionUtils.GenerateQuestion();
    // 这里不需要等待QuestionAsync完成,因为它只是发送消息并注册回调
    _ = DiscordUtils.QuestionAsync(_interactive, _context, question, embed, timeout);
    
    // 异步等待用户的响应,这不会阻塞线程
    bool response = await QuestionUtils.QuestionResponse(question);
    Console.WriteLine(response ? "yes" : "no");
}

3. 保持DiscordUtils.QuestionAsync不变

你的QuestionAsync实现不需要改动,它仍然负责注册反应回调,回调会在用户点击表情时调用SetResponse,直接完成对应的TaskCompletionSource。

为什么这个方案有效?

  • 原来的Thread.Sleep循环会阻塞当前线程,导致Discord的事件处理(比如反应回调)无法及时执行。而await tcs.Task会让出线程,让线程可以去处理其他任务(比如Discord的回调),当回调完成TaskCompletionSource后,await会恢复执行后续代码。
  • TaskCompletionSource是.NET中实现异步等待的常用方式,它允许我们在外部控制任务的完成时机,完美适配这种“等待用户交互”的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:17:13