C# 在没有明确检查的情况下取消等待任务

C# 在没有明确检查的情况下取消等待任务,c#,.net,asynchronous,async-await,C#,.net,Asynchronous,Async Await,我正在开发一个在线游戏服务器,它使用C#作为NPC脚本。为了获得无状态的NPC,我使用枚举器,当我必须等待客户机的响应时,就会让步,并在得到响应后调用MoveNext Talk() Msg("test"); Select("Yes", "No"); yield return true; Msg(Response); (简化的例子是,屈服在现实中稍微复杂一些。) 这很好,但是异步/等待会使它更干净、更容易、更灵活 Talk() Msg("test");

我正在开发一个在线游戏服务器,它使用C#作为NPC脚本。为了获得无状态的NPC,我使用枚举器,当我必须等待客户机的响应时,就会让步,并在得到响应后调用MoveNext

Talk()
    Msg("test");
    Select("Yes", "No");
    yield return true;
    Msg(Response);
(简化的例子是,屈服在现实中稍微复杂一些。)

这很好,但是异步/等待会使它更干净、更容易、更灵活

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对我来说太单调乏味了。服务器软件也会被新手使用,他们可能会忘记这样的检查,这可能会导致问题,这取决于脚本的功能


我现在真正的问题是,是否有人能想出一种方法来取消任务,而不必在脚本本身中进行显式检查。

这是一个简短的答案


如果要安全中止,任务取消必须始终是合作取消。

这是一个简短的回答


如果要安全中止,任务取消必须始终是协同取消。

您可以安装一个自定义的
SynchronizationContext
来取消。取消时,它将停止呼叫continuations。
async
方法将在下一个
wait
之后立即有效停止。如果没有什么东西能坚持完成这些任务,他们就会被解雇,然后离开


请注意,这样即使最后
块也不会执行<代码>使用
将不会进行清理等。在这些情况下,必须对代码进行硬化以确保安全。

您可以安装自定义的同步上下文,可以取消。取消时,它将停止呼叫continuations。
async
方法将在下一个
wait
之后立即有效停止。如果没有什么东西能坚持完成这些任务,他们就会被解雇,然后离开


请注意,这样即使最后
块也不会执行<代码>使用
清理将不会发生等。在这些情况下,您的代码必须经过加固以确保安全。

TPL具有内置的取消功能

让所有函数都接受一个
CanellationToken
,并在调用每个异步函数时传递该令牌


不时在函数内部调用token.ThrowIfCancellationRequested(),整个异步调用链将中止,直到您处理取消。

TPL具有内置的取消功能

让所有函数都接受一个
CanellationToken
,并在调用每个异步函数时传递该令牌


在函数内部,不时调用
token.ThrowIfCancellationRequested()
,整个异步调用链将中止,直到您处理取消。

使用TPL的内置取消功能;请参阅
CancellationTokenSource
。这需要检查脚本,因为任务将返回,“父”函数不应继续执行。否;等待已取消的任务也会取消呼叫者。您只需要一个周期性的
标记。ThrowIfCancellationRequested()
调用。哦,误解了该类的功能。这正是我需要的!我现在在开始时创建一个令牌(脚本类的私有字段),并将其传递给我在Select方法中使用的信号量lim,我用它来等待响应。如果我得到close NPC数据包,我会取消令牌,这会抛出并让我回到start方法。在我自己的封闭方法中,我自己抛出异常,以便快速退出。我看不出这有什么问题,它对脚本编写者隐藏了一切。如果您添加答案,我将接受:)使用TPL的内置取消功能;请参阅
CancellationTokenSource
。这需要检查脚本,因为任务将返回,“父”函数不应继续执行。否;等待已取消的任务也会取消呼叫者。您只需要一个周期性的
标记。ThrowIfCancellationRequested()
调用。哦,误解了该类的功能。这正是我需要的!我现在在开始时创建一个令牌(脚本类的私有字段),并将其传递给我在Select方法中使用的信号量lim,我用它来等待响应。如果我得到close NPC数据包,我会取消令牌,这会抛出并让我回到start方法。在我自己的封闭方法中,我自己抛出异常,以便快速退出。我看不出这有什么问题,它对脚本编写者隐藏了一切。如果你加上答案,我会接受的:)@Aron任何一个适合你的。我是一个机会均等的拼写者。@Aron任何一个为你工作的人。我是一个机会均等的拼写者。虽然看起来我不会使用这种方法,但阅读SynchronizationContext仍然非常有趣,从未听说过(可能是因为我很少做GUI编程)。这是一个非常有趣的想法,+1。虽然看起来我不会使用这种方法,但阅读SynchronizationContext仍然非常有趣,从未听说过(可能是因为我很少做GUI编程)。