草庐IT

c# - 我如何处理不想等待的异步任务?

coder 2024-05-30 原文

我正在编写一个多人游戏服务器,并且正在研究新的 C# async/await 功能可以实现的方式 帮我。服务器的核心是一个循环,它会尽可能快地更新游戏中的所有 Actor 可以:

while (!shutdown)
{
    foreach (var actor in actors)
        actor.Update();

    // Send and receive pending network messages
    // Various other system maintenance
}

此循环需要处理数千个 Actor 并每秒更新多次以保持 游戏运行流畅。有些 Actor 偶尔会在他们的更新功能中执行缓慢的任务,例如 从数据库中获取数据,这是我想使用异步的地方。一旦检索到此数据 actor 想要更新游戏状态,这必须在主线程上完成。

由于这是一个控制台应用程序,我计划编写一个可以调度的 SynchronizationContext 等待主循环的委托(delegate)。这允许这些任务在完成后更新游戏 并让未处理的异常被抛入主循环。我的问题是,如何编写异步 更新功能?这非常有效,但违反了不使用 async void 的建议:

Thing foo;

public override void Update()
{
    foo.DoThings();

    if (someCondition) {
        UpdateAsync();
    }
}

async void UpdateAsync()
{
    // Get data, but let the server continue in the mean time
    var newFoo = await GetFooFromDatabase();

    // Now back on the main thread, update game state
    this.foo = newFoo;
}

我可以使 Update() 异步并将任务传播回主循环,但是:

  • 我不想为永远不会使用它的数千个更新增加开销。
  • 即使在主循环中,我也不想等待任务并阻塞循环。
  • 等待任务无论如何都会导致死锁,因为它需要在等待线程上完成。

我该如何处理所有这些迫不及待的任务?唯一一次我可能想知道他们都 完成是当我关闭服务器时,但我不想收集由生成的每个任务 可能需要数周的更新时间。

最佳答案

我的理解是,关键在于你想要:

while (!shutdown)
{
    //This should happen immediately and completions occur on the main thread.
    foreach (var actor in actors)
        actor.Update(); //includes i/o bound database operations

    // The subsequent code should not be delayed
   ...
}

while 循环在主控制台线程中运行的位置。这是一个紧密的单线程循环。您可以并行运行 foreach,但您仍将等待运行时间最长的实例(从数据库获取数据的 i/o 绑定(bind)操作)。

await async 不是此循环中的最佳选择,您需要在线程池上运行这些 i/o 数据库任务。在线程池上,async await 有助于释放池线程。

因此,下一个问题是如何将这些完成返回到您的主线程。好吧,您似乎需要类似于主线程上的消息泵的东西。参见 this post有关如何执行此操作的信息,尽管这可能有点笨拙。您可以只拥有一个完成队列,您可以在每次通过 while 循环时检查主线程。您将使用 concurrent data structures 之一执行此操作以使其完全线程安全,然后在需要设置 Foo 时设置它。

似乎有一些空间可以合理化这种 actors 和线程的轮询,但在不知道应用程序细节的情况下很难说。

几点:-

  • 如果您没有 Wait 更高的任务,您的主控制台线程将退出,您的应用程序也将退出。参见 here了解详情。

  • 正如您所指出的,await async 不会阻塞当前线程,但它确实意味着 await 之后的代码只会在 await 完成时执行。

  • 完成可能会也可能不会在调用线程上完成。 Synchronization Context 你已经提到了,我就不多说了。

  • 控制台应用程序上的同步上下文为空。参见 here以供引用。

  • 异步并非真正适用于即发即弃类型的操作。

对于即发即忘,您可以根据您的情况使用以下选项之一:

  • 使用 Task.Run 或 Task.StartNew。参见 here for differences .
  • 对在您自己的线程池下运行的长时间运行场景使用生产者/消费者类型模式。

请注意以下事项:-

  • 您需要处理衍生任务/线程中的异常。如果有任何您没有观察到的异常,您可能想要处理这些异常,甚至只是记录它们的发生。查看有关 unobserved exceptions 的信息.
  • 如果您的进程在这些长时间运行的任务在队列中或启动时终止,它们将不会运行,那么您可能需要某种持久性机制(数据库、外部队列、文件)来跟踪这些操作的状态.

如果您想知道这些任务的状态,那么您将需要以某种方式跟踪它们,无论是在内存列表中,还是通过查询您自己的线程池的队列或通过查询持久化机制。持久性机制的好处是它对崩溃具有弹性,在关闭期间你可以立即关闭,然后在你重新启动时从你结束的地方开始(这​​当然取决于任务运行的重要性)一定的时间范围)。

关于c# - 我如何处理不想等待的异步任务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18710079/

有关c# - 我如何处理不想等待的异步任务?的更多相关文章

  1. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  2. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  3. ruby - 如何使用 RSpec::Core::RakeTask 创建 RSpec Rake 任务? - 2

    如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake

  4. ruby-on-rails - Enumerator.new 如何处理已通过的 block ? - 2

    我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m

  5. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  6. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  7. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  8. ruby-on-rails - Rake 任务仅调用一次时执行两次 - 2

    我写了一个非常简单的rake任务来尝试找到这个问题的根源。namespace:foodotaskbar::environmentdoputs'RUNNING'endend当在控制台中执行rakefoo:bar时,输出为:RUNNINGRUNNING当我执行任何rake任务时会发生这种情况。有没有人遇到过这样的事情?编辑上面的rake任务就是写在那个.rake文件中的所有内容。这是当前正在使用的Rakefile。requireFile.expand_path('../config/application',__FILE__)OurApp::Application.load_tasks这里

  9. ruby-on-rails - 如何处理 Grape 中特定操作的过滤器之前? - 2

    我正在我的Rails项目中安装Grape以构建RESTfulAPI。现在一些端点的操作需要身份验证,而另一些则不需要身份验证。例如,我有users端点,看起来像这样:moduleBackendmoduleV1classUsers现在如您所见,除了password/forget之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如passwords并且只是删除password/forget从逻辑上讲,这个端点应该与用户资源。问题是Grapebefore过滤器没有像except,only这样的选项,我可以在其中说对某些操作应用过滤器。您通常如何干净利落地处理这种情况?

  10. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

    在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

随机推荐