Boost.Asio - 使用 make_strand 时何时需要显式链包装

Boost.Asio - when is explicit strand wrapping needed when using make_strand

我一直在研究 Boost.Asio 和 Boost.Beast,并且对 socket::async_* 成员函数调用何时需要显式链环绕感到有些困惑。

在Boost.Asio(1.78)中,有一个make_strand函数。 Boost.Beast 提供的示例显示它是这样使用的:

server/chat-multi/listener.cpp

void
listener::
run()
{
    // The new connection gets its own strand
    acceptor_.async_accept(
        net::make_strand(ioc_),
        beast::bind_front_handler(
            &listener::on_accept,
            shared_from_this()));
}

//...

// Handle a connection
void
listener::
on_accept(beast::error_code ec, tcp::socket socket)
{
    if(ec)
        return fail(ec, "accept");
    else
        // Launch a new session for this connection
        boost::make_shared<http_session>(std::move(socket), state_)->run();

    // The new connection gets its own strand
    acceptor_.async_accept(
        net::make_strand(ioc_),
        beast::bind_front_handler(
            &listener::on_accept,
            shared_from_this()));
}

server/chat-multi/http_session.cpp

void
http_session::
run()
{
    do_read();
}

//...

void
http_session::
do_read()
{
    // Construct a new parser for each message
    parser_.emplace();

    // Apply a reasonable limit to the allowed size
    // of the body in bytes to prevent abuse.
    parser_->body_limit(10000);

    // Set the timeout.
    stream_.expires_after(std::chrono::seconds(30));

    // Read a request
    http::async_read(
        stream_,
        buffer_,
        parser_->get(),
        beast::bind_front_handler(
            &http_session::on_read,
            shared_from_this()));
}

void
http_session::
on_read(beast::error_code ec, std::size_t)
{
    // This means they closed the connection
    if(ec == http::error::end_of_stream)
    {
        stream_.socket().shutdown(tcp::socket::shutdown_send, ec);
        return;
    }

    // Handle the error, if any
    if(ec)
        return fail(ec, "read");

    // See if it is a WebSocket Upgrade
    if(websocket::is_upgrade(parser_->get()))
    {
        // Create a websocket session, transferring ownership
        // of both the socket and the HTTP request.
        boost::make_shared<websocket_session>(
            stream_.release_socket(),
                state_)->run(parser_->release());
        return;
    }
    //...
}

server/chat-multi/websocket_session.cpp

void
websocket_session::
on_read(beast::error_code ec, std::size_t)
{
    // Handle the error, if any
    if(ec)
        return fail(ec, "read");

    // Send to all connections
    state_->send(beast::buffers_to_string(buffer_.data()));

    // Clear the buffer
    buffer_.consume(buffer_.size());

    // Read another message
    ws_.async_read(
        buffer_,
        beast::bind_front_handler(
            &websocket_session::on_read,
            shared_from_this()));
}

在同一个 Boost.Beast 示例中,对套接字的 async_read 成员函数的后续调用是通过 postdispatch(使用 socket::get_executor)或使用 strand::wrap.

包装完成处理程序

根据的回答,似乎是make_strand函数将executor复制到socket对象中,默认情况下socket对象的completion 处理程序将在同一链上调用。以 socket::async_receive 为例,这对我来说意味着有两点工作要做:

A) socket::async_receive I/O 工作本身

B) 调用完成处理程序所涉及的工作

我的问题是:

  1. 根据链接的答案,当使用 make_strand 时,保证在同一链上调用 B,但不会调用 A。这是正确的,还是我误解了什么?

  2. 如果 1) 是正确的,为什么上面提供的 server/chat-multi 示例没有明确地将 async_read 工作放在一个链上?

  3. 在 Michael Caisse 的 cppcon 2016 演讲“使用 Boost.Asio 的异步 IO”中,他也没有明确地将 async_read_until 操作包装在一个链中。他解释说写调用应该与链同步,因为理论上它们可以从应用程序中的任何线程调用。但是 read 调用不会,因为他自己控制它们。这如何融入图片?

提前致谢

如果未指定或绑定执行器,则使用“关联的执行器”。

对于成员异步启动函数,默认执行程序是来自 IO 对象的执行程序。在您的情况下,它将是在 strand 执行器“上”(与)创建的套接字。换句话说,socket.get_executor() 已经 returns strand<> 执行者。

只有在发布时您才需要指定链执行器(将处理程序绑定到它,因此它成为处理程序的隐式默认值):

  • Why is boost::asio::io service designed to be used as a parameter?