【问题标题】:asio::async_write and strandasio::async_write 和 strand
【发布时间】:2016-07-01 04:18:16
【问题描述】:
asio::async_write(m_socket, asio::buffer(buf, bytes),
                custom_alloc(m_strand.wrap(custom_alloc(_OnSend))));

此代码是否保证 async_write 中的所有异步操作处理程序(对 async_write_some 的调用)都是通过 strand 调用的? (或者它只是为了my_handler?)

【问题讨论】:

    标签: c++ multithreading boost boost-asio


    【解决方案1】:

    使用以下代码:

    asio::async_write(stream, ..., custom_alloc(m_strand.wrap(...)));
    

    对于这个组合操作,如果满足以下所有条件,所有对stream.async_write_some() 的调用都将在m_strand 内调用:

    • 发起的async_write(...)调用在m_strand()内运行:

      assert(m_strand.running_in_this_thread());
      asio::async_write(stream, ..., custom_alloc(m_strand.wrap(...)));
      
    • custom_alloc 的返回类型是:

      • strand::wrap()返回的确切类型

        template <typename Handler> 
        Handler custom_alloc(Handler) { ... }
        
      • 一个自定义处理程序,用于链接 asio_handler_invoke() 的调用:

        template <class Handler>
        class custom_handler
        {
        public:
          custom_handler(Handler handler)
            : handler_(handler)
          {}
        
          template <class... Args>
          void operator()(Args&&... args)
          {
            handler_(std::forward<Args>(args)...);
          }
        
          template <typename Function>
          friend void asio_handler_invoke(
            Function intermediate_handler,
            custom_handler* my_handler)
          {
            // Support chaining custom strategies incase the wrapped handler
            // has a custom strategy of its own.
            using boost::asio::asio_handler_invoke;
            asio_handler_invoke(intermediate_handler, &my_handler->handler_);
          }
        
        private:
          Handler handler_;
        };
        
        template <typename Handler>
        custom_handler<Handler> custom_alloc(Handler handler)
        {
          return {handler};
        }
        

    有关股线的更多详细信息,请参阅this 答案,有关asio_handler_invoke 的详细信息,请参阅this 答案。

    【讨论】:

    • 请提供“所有对 stream.async_write_some() 的调用都将在 m_strand 中调用”的源代码或文档参考。 OP 接受了这个答案,因为他希望这是真的,但我认为不是。
    • 看我的 cmets 来回答你的问题。必须这样,否则从 SSL 开始,各种事情都行不通。如果套接字同时变得可读和可写,则两个线程(一个为异步读取执行组合操作,另一个为异步写入执行组合操作)可能同时访问相同的 SSL 上下文,程序将崩溃。这是 ASIO 的一个核心特性,它可以让各种事情发挥作用。转到here 并阅读以“在组合的情况下”开头的段落。
    • @PSIAlt Strands: Use Threads Without Explicit Locking 页面记录了此行为。在这个answer 中,我demonstrate 中间处理程序在最终完成处理程序的钩子中被调用。
    • 我喜欢在帖子发布后几天进行的这些微小的挑剔编辑:) 非常相关。让答案更有价值。再次感谢您,坦纳!
    • @Tarc 第一次调用stream.async_write_some() 将发生在async_read() 初始化函数中。另一方面,后续对组合操作的stream.async_write_some() 调用将发生在与最终完成处理程序在相同上下文中运行的中间处理程序中。
    【解决方案2】:

    UPD:这个答案是错误的=)对于任何感兴趣的人,您可以查看 cmets 尾部的详细信息,感谢 Tanner Sansbury


    没有。这保证了您的 完成处理程序 将被 strand 锁调用。这不包括对async_write_some 的调用。

    另外 boost::asio 没有“写队列”之类的东西,你需要管理 async_write 调用以防止写入混合内容。

    【讨论】:

    • 问题不是并发写入,而是同时读取和写入。 SSL 连接只有一个上下文,同时读取和写入会破坏 SSL 上下文。这就是为什么要包裹在一个链中,并且组合的 I/O 函数通过asio_handler_invoke 方案继承保护。否决票是恰当的——您的回答反映了对组合中间操作如何分派以及 strand 的魔法调用方案如何保护它们的完全误解。
    • 听听ASIO作者的。听1:18:00 to 1:18:50,因为他明确指出链保护内部组合操作,从而保护与流和连接相关的所有结构。这允许整个连接及其所有关联状态都像单线程一样运行。
    • @PSIAlt David 似乎高度依赖设计知识及其背后的概念。即使您的分析等于在async_write 的实现中发现了一个缺陷(不太可能),这也会使其成为一个错误,并且答案仍然是不正确的。 [这里的谬误是假设您可以通过查看实现细节而不是理解高级概念来更好地评估复杂库的工作。这种可能性很小。]
    • 关于实现细节,如果完成条件没有满足,那么write_op会将自身作为中间完成处理程序传递给一个async_write_some 操作。当reactive_socket_send_op 完成时,它将调用asio_handler_invoke(bound_handler, write_op) 调用asio_handler_invoke(bound_handler, final_completion_handler) 调用wrapped_handler.dispatcher.dispatch(bound_handler) 其中dispatcher 是一个strandbound_handler 是一个绑定处理程序,它调用write_opec 和@ 987654337@.
    • 自编辑以来,我对这个答案投了赞成票,因为它不再具有误导性,而且我认为保持评论交流很好!感谢大家耐心地坚持事实。 /cc @DavidSchwartz
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-22
    • 1970-01-01
    • 2018-09-22
    • 1970-01-01
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多