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
相关产品推荐
相关产品推荐

