c# - TaskCompletionSource 无法处理多个等待的任务 - 我做错了什么?

标签 c# async-await godot

我正在使用 Godot 游戏引擎和 Mono/C# 开发一款游戏。 我正在努力实现以下目标:

  • 在屏幕上显示消息
  • 等待鼠标按钮点击/屏幕点击
  • 显示另一条消息
  • 等待点击
  • ...

因此我有一个 Say() 方法:

async Task Say(string msg)
{
  SetStatusText(msg);
  _tcs = new TaskCompletionSource<Vector2>();
  await _tcs.Task;
  SetStatusText(string.Empty);
}

我期待的是这个工作:

async Task Foo()
{
  // Displays "First".
  await Say("First");
  // "Second" should be shown after a click.
  await Say("Second");
  // "Third" should be shown after another click.
  await Say("Third");
}

实际发生的情况是:

  • 显示“第一个”
  • 点击后会显示“第二个”。
  • “第三个”永远不会出现,即使在点击之后也是如此。

我追踪到_tcsnull(或者处于无效状态,如果我没有将其设置为null)我的鼠标按钮点击代码:

public void OnMouseButtonClicked(Vector2 mousePos)
{
  if(_tcs  != null)
  {
    _tcs.SetResult(mousePos);
    _tcs = null;
    return;
  }

  // Other code, executed if not using _tcs.
}

鼠标按钮单击代码设置_tcs的结果,这对于第一个await工作正常,但随后它失败了,尽管我正在创建一个新的实例TaskCompletionSource 每次调用 Say() 时。

Godot 问题还是我的 C# 异步知识变得如此生疏以至于我在这里遗漏了一些东西?几乎感觉 _tcs 正在被捕获并重用。

最佳答案

has my C# async knowledge become so rusty that I'm missing something here?

这是 await 的一个棘手的角落: 延续是同步安排的。我对此进行了更多描述on my blog并在 this single-threaded deadlock example .

关键要点是 TaskCompletionSource<T>将在返回之前调用延续,这包括具有 await 的延续方法编辑了该任务。

遍历:

  • Foo调用Say第一次。
  • Say等待_tcs.Task ,它不完整,因此它返回一个不完整的任务。
  • Foo等待从 Say 返回的任务,并返回一个未完成的任务。
  • 用户点击并 OnMouseButtonClicked被调用。
  • OnMouseButtonClicked来电 _tcs.SetResult 。这不仅完成了任务,还运行了任务的延续。
  • 这意味着 Say 的剩余部分方法被执行。如果将断点放置在 SetStatusText(string.Empty) ,你会看到线程堆栈有 OnMouseButtonClickedSetResult在里面!
  • Say 的末尾方法,其任务已完成,并且该任务的延续被执行。
  • 这意味着Foo继续执行 - 从 OnMouseButtonClicked .
  • Foo来电 Say第二次,设置 _tcs并等待任务。由于该任务尚未完成,Say返回未完成的任务。
  • Foo等待该任务,返回OnMouseButtonClicked .
  • OnMouseButtonClicked SetResult 之后继续执行生产线和套件_tcsnull .

这种synchronous continuation doesn't always happen ,但是一旦发生就很烦人。一种简单的解决方法是传递 TaskCreationOptions.RunContinuationsAsynchronouslyTaskCompletionSource<T>构造函数。

关于c# - TaskCompletionSource 无法处理多个等待的任务 - 我做错了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70565248/

相关文章:

c# - async/await - 我使用了错误的同步上下文吗?

godot - 在 Godot 中,当节点不在父节点正上方时,可以隐藏节点吗?

c# - 基于查询字符串的行为

c# - 显示调试器未显示在 Visual Studio 2017 中

c# - 如何让角色扮演游戏中的生物相互追踪

game-engine - 检测敌人是否被子弹击中

godot - 检查一个对象是否属于给定字符串中的类名的类?

c# - 如果我使用类似 NHibernate 的 ORM,为什么还需要 LINQ?

c# - 我的 Controller 方法是异步执行的吗?

.net-3.5 - Async/await for Compact Framework v3.5 - 手动实现