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
成员函数的后续调用是通过 post
、dispatch
(使用 socket::get_executor
)或使用 strand::wrap
.
包装完成处理程序
根据的回答,似乎是make_strand
函数将executor复制到socket对象中,默认情况下socket对象的completion 处理程序将在同一链上调用。以 socket::async_receive
为例,这对我来说意味着有两点工作要做:
A) socket::async_receive
I/O 工作本身
B) 调用完成处理程序所涉及的工作
我的问题是:
根据链接的答案,当使用 make_strand
时,保证在同一链上调用 B,但不会调用 A。这是正确的,还是我误解了什么?
如果 1) 是正确的,为什么上面提供的 server/chat-multi 示例没有明确地将 async_read
工作放在一个链上?
在 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?
我一直在研究 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
成员函数的后续调用是通过 post
、dispatch
(使用 socket::get_executor
)或使用 strand::wrap
.
根据make_strand
函数将executor复制到socket对象中,默认情况下socket对象的completion 处理程序将在同一链上调用。以 socket::async_receive
为例,这对我来说意味着有两点工作要做:
A) socket::async_receive
I/O 工作本身
B) 调用完成处理程序所涉及的工作
我的问题是:
根据链接的答案,当使用
make_strand
时,保证在同一链上调用 B,但不会调用 A。这是正确的,还是我误解了什么?如果 1) 是正确的,为什么上面提供的 server/chat-multi 示例没有明确地将
async_read
工作放在一个链上?在 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?