草庐IT

c++ - 试图了解Boost.Asio定制服务实现

coder 2023-05-03 原文

我正在考虑在我们当前正在使用的现有专有第三方网络协议(protocol)之上编写自定义Asio服务。

根据Highscore Asio guide的介绍,您需要实现三个类来创建自定义Asio服务:

  • boost::asio::basic_io_object派生的类,表示新的I/O对象。
  • boost::asio::io_service::service派生的类,表示已在I/O服务中注册并且可以从I/O对象访问的服务。
  • 一个不派生自表示服务实现的任何其他类的类。

  • 网络协议(protocol)实现已经提供了异步操作,并具有(阻塞)事件循环。因此,我想将其放入服务实现类中,并在内部工作线程中运行事件循环。到现在为止还挺好。

    查看自定义服务的一些示例,我注意到服务类产生了自己的内部线程(实际上,它们实例化了自己的内部io_service实例)。例如:
  • “高分”页面提供了directory monitor example。它本质上是inotify的包装。有趣的类是inotify/basic_dir_monitor_service.hppinotify/dir_monitor_impl.hppDir_monitor_impl处理具有阻塞性的inofity的实际交互,因此将在后台线程中运行。我同意这一点。但是basic_dir_monitor_service也有一个内部工作线程,所有似乎要做的就是在main的io_servicedir_monitor_impl之间重新整理请求。我试用了该代码,在basic_dir_monitor_service中删除了工作线程,而是直接将请求发布到主io_service上,该程序仍然像以前一样运行。
  • 在Asio的custom logger service example中,我注意到了相同的方法。 logger_service 产生一个内部工作线程来处理日志记录请求。我还没有时间来处理这些代码,但是我认为,也应该可以将这些请求直接发布到主io_service上。

  • 拥有这些“中介 worker ”有什么好处?您不能一直将所有工作发布到主io_service吗?我是否错过了Proactor模式的某些关键方面?

    我可能应该提到我正在为功能不足的单核嵌入式系统编写软件。这些额外的线程就位似乎只是施加了不必要的上下文切换,如果可能的话,我想避免这些切换。

    最佳答案

    简而言之,一致性。这些服务试图满足Boost.Asio提供的服务所提出的用户期望。

    使用内部io_service可以清楚地分离所有权和对处理程序的控制。如果定制服务将其内部处理程序发布到用户的io_service中,则该服务的内部处理程序的执行将与用户的处理程序隐式耦合。通过Boost.Asio Logger Service示例考虑这将如何影响用户期望:

  • logger_service写入处理程序中的文件流。因此,从不处理io_service事件循环的程序(例如仅使用同步API的程序)将永远不会写入日志消息。
  • logger_service将不再是线程安全的,如果io_service由多个线程处理,则可能会调用未定义的行为。
  • logger_service的内部操作的生存期受到io_service的生存期的限制。例如,当调用服务的shutdown_service()函数时,拥有的io_service的生存期已经结束。因此,无法通过logger_service::log()中的shutdown_service()记录消息,因为它将尝试将内部处理程序发布到生存期已经结束的io_service中。
  • 用户可能不再假定操作和处理程序之间是一对一的映射。例如:
    boost::asio::io_service io_service;
    debug_stream_socket socket(io_service);
    boost::asio::async_connect(socket, ..., &connect_handler);
    io_service.poll();
    // Can no longer assume connect_handler has been invoked.
    

    在这种情况下,io_service.poll()可以调用logger_service内部的处理程序,而不是connect_handler()

  • 此外,这些内部线程试图模仿Boost.Asio itself内部使用的行为:

    The implementation of this library for a particular platform may make use of one or more internal threads to emulate asynchronicity. As far as possible, these threads must be invisible to the library user.



    Directory Monitor example

    在目录监视器示例中,内部线程用于防止在等待事件时无限期地阻止用户的io_service。一旦发生事件,就可以调用完成处理程序,因此内部线程将用户处理程序发布到用户的io_service中以进行延迟的调用。该实现通过内部线程对用户几乎不可见的方式来模拟异步性。

    有关详细信息,当通过dir_monitor::async_monitor()启动异步监视操作时,会将basic_dir_monitor_service::monitor_operation发布到内部io_service中。调用时,此操作将调用dir_monitor_impl::popfront_event(),这可能是阻塞的调用。因此,如果将monitor_operation发布到用户的io_service中,则可能会无限期地阻止用户的线程。考虑对以下代码的影响:

    boost::asio::io_service io_service;
    boost::asio::dir_monitor dir_monitor(io_service); 
    dir_monitor.add_directory(dir_name); 
    // Post monitor_operation into io_service.
    dir_monitor.async_monitor(...);
    io_service.post(&user_handler);
    io_service.run();
    

    在上面的代码中,如果io_service.run()首先调用monitor_operation,则在user_handler()观察到dir_monitor目录上的事件之前,不会调用dir_name。因此,dir_monitor服务的实现不会以大多数用户期望的其他服务一致的方式运行。

    Asio Logger Service

    使用内部线程和io_service:
  • 通过在内部线程内执行潜在的阻塞或昂贵的调用来减轻登录用户线程的开销。
  • 保证std::ofstream的线程安全,因为只有单个内部线程写入流中。如果日志记录是直接在logger_service::log()中完成的,或者logger_service将其处理程序发布到用户的io_service中,则为了线程安全性,将需要显式同步。其他同步机制可能会在实现中引入更大的开销或复杂性。
  • 允许 services 记录 shutdown_service() 中的消息。在destruction期间,io_service将:
  • 关闭每个服务。
  • 销毁所有计划在io_service或其任何关联的strand中进行延迟调用的未调用处理程序。
  • 销毁其每个服务。

  • 由于用户的io_service的生存期已结束,因此既未处理其事件队列,也无法发布其他处理程序。通过拥有自己的内部io_service(由自己的线程处理),logger_service使其他服务可以在shutdown_service()期间记录消息。

    其他注意事项

    实现定制服务时,请注意以下几点:
  • 阻止内部线程上的所有信号。
  • 切勿直接调用用户代码。
  • 在实现被销毁时,如何跟踪和发布用户处理程序。
  • 服务拥有的资源,在服务的实现之间共享。

  • 对于最后两点,dir_monitor I/O对象表现出用户可能无法预期的行为。当服务中的单个线程在单个实现的事件队列上调用阻止操作时,它有效地阻止了可能针对其相应实现立即完成的操作:

    boost::asio::io_service io_service;
    boost::asio::dir_monitor dir_monitor1(io_service); 
    dir_monitor1.add_directory(dir_name1); 
    dir_monitor1.async_monitor(&handler_A);
    
    boost::asio::dir_monitor dir_monitor2(io_service); 
    dir_monitor2.add_directory(dir_name2); 
    dir_monitor2.async_monitor(&handler_B);
    // ... Add file to dir_name2.
    
    {
      // Use scope to enforce lifetime.
      boost::asio::dir_monitor dir_monitor3(io_service); 
      dir_monitor3.add_directory(dir_name3); 
      dir_monitor3.async_monitor(&handler_C);
    }
    io_service.run();
    

    尽管不会阻止与handler_B()(成功)和handler_C()(中止)相关联的操作,但是basic_dir_monitor_service中的单线程被阻止,等待dir_name1的更改。

    关于c++ - 试图了解Boost.Asio定制服务实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23887056/

    有关c++ - 试图了解Boost.Asio定制服务实现的更多相关文章

    1. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

      我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

    2. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

      我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

    3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    4. ruby-on-rails - 启动 Rails 服务器时 ImageMagick 的警告 - 2

      最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru

    5. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

      在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

    6. ruby - 如何进行排列以有效地定制输出 - 2

      这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][

    7. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

      我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

    8. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

      我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

    9. ruby-on-rails - 在 Rails 中调试生产服务器 - 2

      您如何在Rails中的实时服务器上进行有效调试,无论是在测试版/生产服务器上?我试过直接在服务器上修改文件,然后重启应用,但是修改好像没有生效,或者需要很长时间(缓存?)我也试过在本地做“脚本/服务器生产”,但是那很慢另一种选择是编码和部署,但效率很低。有人对他们如何有效地做到这一点有任何见解吗? 最佳答案 我会回答你的问题,即使我不同意这种热修补服务器代码的方式:)首先,你真的确定你已经重启了服务器吗?您可以通过跟踪日志文件来检查它。您更改的代码显示的View可能会被缓存。缓存页面位于tmp/cache文件夹下。您可以尝试手动删除

    10. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

      华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

    随机推荐