【问题标题】:Implementing stateless NPC scripting with coroutines/C# 5's await使用协程/C# 5 的 await 实现无状态 NPC 脚本
【发布时间】:2011-12-30 20:20:40
【问题描述】:

我正在尝试使用 C# 的新 await 功能来实现 NPC 脚本。 This is my proof of concept.

NPC.cs你可以看到这个sn-p:

public async void Run(INPC npc)
{
    npc.Say("Hello!");
    await npc.WaitForOk();
    npc.Say("This is an example of some weird crap.");
    await npc.WaitForOk();
    npc.Say("Bye.");
    await npc.WaitForOk();
}

在一个真实示例中,该脚本将使用 IronPython 之类的脚本语言来实现。虽然以后可能会支持async/await关键字,但每次通话都必须这样做非常麻烦和烦人。

我尝试让另一种方法异步并执行等待,并让脚本简单地调用它,但由于 async/await 的工作方式,脚本方法 (Run) 将继续没有暂停/阻塞。

有没有办法避免使脚本方法异步并且必须在每次调用前使用await,同时仍保留类似协程的功能?

另外,如果有比使用async/await 同时仍然保持线程效率更好的解决方案,请突出显示它。

谢谢!

【问题讨论】:

    标签: c# scripting async-await c#-5.0


    【解决方案1】:

    async/await 的用法非常有趣。

    我从网上收集了很多与语法相关的问题到one of my blog posts。简而言之,asyncawait 都是必需的,这是有充分理由的。

    如果您愿意为您的 NPC 添加状态,请考虑使用基于观察者的方法 (Rx)。这种方法能够进行复杂的交互,并且还可以允许不同 NPC 的并行执行。

    您还可以在线程池任务上为您的 NPC 设置模式,这非常有效 - 当它们阻塞时,CPU 可用于其他任务。

    【讨论】:

    • 我实际上已经考虑过使用带有 AutoResetEvents 的 ThreadPool;每当调用NPC.WaitForOk() 时,它会将请求发送到客户端,然后发送到WaitOne()。我从未见过具有这种行为的 ThreadPool 线程。the MSDN documentation even states the opposite.
    • 你是对的 - 线程池线程会阻塞。我的意思是说 CPU 可用。
    • 啊..这更有意义。但在实际应用中,可能有成百上千个 NPC 对话同时发生;一次拥有这么多被阻塞的线程只是感觉..'hacky'。不过,我会检查 Rx,但在选择我接受的答案之前,我会等待其他答案。谢谢!
    • @angelsl,这不仅是hacky,也是一个非常糟糕的主意。每个线程消耗 1 MB 内存(默认情况下)。因此,拥有一千个 NPC,每个都有自己的线程,这意味着不必要地消耗 1 GB 的内存。 THreadPool 真的不适合这样的事情。
    • @svick:我也是这么想的。嗯嗯。
    【解决方案2】:

    我不知道你为什么要如此努力地避免await。我认为对这种代码要求它是一个好主意,因为很难说出代码实际上做了什么。所以,我的看法是,以这种方式使用await 可能是你最好的选择。

    此外,如果可以的话,最好避免使用 async void 方法,因为您无法捕获它们抛出的异常。

    我可以想象有一些方法可以避免写async,比如:

    npc.AddAction(n => n.Say("Hello!"))
       .AddAction(n => n.WaitForOk())
       .AddAction(n => n.Say("This is an example of some weird crap."))
       .AddAction(n => n.WaitForOk());
    
    npc.Run();
    

    在这里,Run() 处理使用AddAction() 构建的操作列表,并在必要时使用await

    但我怀疑你是否可以像使用 awaits 的代码一样简单。 (另外,在这样的代码中实现任何类型的控制流都会使它变得非常不可读。)

    【讨论】:

    • 我尽量避免在任何地方使用await,因为在真正的游戏服务器中,脚本将通过 IronPython 或 IronRuby 之类的东西实现,它可能支持也可能不支持 await/async .此外,WaitForblah 方法将在上下文中使用,并且众所周知它们会“暂停”脚本执行。所以这就是为什么我试图避免到处都有await
    • 如果就这么简单,那么await 就没有必要了,不是吗?什么await 完全重写了方法的代码。你基本上是在问如何用一种不支持它的语言来做到这一点。好吧,如果不使用一些糟糕的技巧,你就做不到。
    猜你喜欢
    • 1970-01-01
    • 2013-05-31
    • 2014-01-09
    • 1970-01-01
    • 2013-07-19
    • 2012-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多