【发布时间】:2014-02-20 15:40:00
【问题描述】:
我正在开发一个使用 C# 编写 NPC 脚本的在线游戏服务器。为了获得无状态的 NPC,我使用了枚举器,当我必须等待来自客户端的响应时让步,并在我得到它后调用 MoveNext。
Talk()
Msg("test");
Select("Yes", "No");
yield return true;
Msg(Response);
(简化示例,实际中的屈服要复杂一些。)
这很好用,但 async/wait 会让它更简洁、更容易、更灵活。
Talk()
Msg("test");
Msg(await Select("Yes", "No"));
玩弄 async/wait 我发现我唯一的问题是脚本不像发送一些消息那么简单。从主 Talk 函数调用其他函数时,我必须等待它们,因为否则执行不会停止。其他函数也可以调用更多,创建未知数量的等待。
Talk()
Msg("test");
await OtherTalk();
Msg("end"); // would be called right away
OtherTalk()
Msg("test2");
Msg(await Select("Yes", "No"));
如果我在这种“潜谈”方法中关闭 NPC,我可能会留下相当多的任务悬而未决,因为我不会返回链条。 NPC 关闭,没有更多响应,任务继续等待。
解决这个问题的方法是让链条恢复原状,在等待此类函数后进行显式检查,以检查 NPC 是否在某处关闭。但我希望它们尽可能简单和直接,坦率地说,在每次调用函数之后都有一个 if 对我来说太乏味了。服务器软件也将被新手使用,他们可能会忘记这样的检查,这可能会导致问题,这取决于脚本的作用。
我现在的实际问题是,是否有人可以想办法取消任务,而无需明确检查脚本本身。
【问题讨论】:
-
使用 TPL 的内置取消功能;见
CancellationTokenSource。 -
这需要在脚本中进行检查,因为任务会返回并且“父”函数不应该继续执行。
-
否;等待取消的任务也会取消调用者。您只需要定期拨打
token.ThrowIfCancellationRequested()电话。 -
哦,误解了那个类的作用。这正是我所需要的!我现在在开始时创建一个令牌(脚本类的私有字段)并将其传递给我在 Select 方法中使用的 SemaphoreSlim,我用它来等待响应。如果我得到关闭的 NPC 数据包,我会取消令牌,这会抛出并让我回到 start 方法。在我自己的 Close 方法中,我自己抛出异常,以便快速退出。我认为这没有任何问题,并且它对脚本编写者隐藏了所有内容。如果您添加答案,我会接受:)
标签: c# .net asynchronous async-await