增强 ASIO 阻止回调

Boost ASIO blocking callbacks

本文关键字:回调 ASIO 增强      更新时间:2023-10-16

从asio到阻止的回调是否安全(或合理(?

void Connection::start() {
/* Assume std::string buffer; that has data to write. */
auto callbackFn = boost::bind(&Connection::callback, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred);
boost::asio::async_write(_socket, boost::asio::buffer(buffer), callBackFn);
}
void Connection::callback(const boost::system::error_code& error,
std::size_t bytesSent) {
/* Perform long running action with bytes sent */
boost::asio::async_write(...);
}

还是我应该在回调中生成一个线程,然后在该线程完成后立即返回并执行操作?

是的,这在许多情况下是合理的。但是,作为架构师,您有责任确定它在您的方案中是否真正合理。

一般来说,您的io_service将在一个线程上运行,这就是它将调用您的回调的线程,这意味着它在调用回调时不会处理消息。如果没关系,那你就可以走了。(请注意,这里出现了"安全"部分 - 您是否正确保护了对象以便跨线程使用?

如果不好,那么您应该将消息发送到另一个线程进行处理。

至于我和我的房子,我们更喜欢活动对象模式,以保持IO服务的响应,并将多线程的担忧降至最低。请参阅 Herb Sutter 关于有效并发的系列文章。具体来说,您应该阅读:

  • "正确使用线程 = 隔离 + 异步消息">
  • "更喜欢使用活动对象而不是裸线程">

PS,如果可能的话,放弃boost::bind(),转而使用 C++11/14 lambda。


更新:至于为什么更喜欢lambda,答案归结为清晰度和可维护性(性能大致相同,lambdas略有优势(。一旦你习惯了用于捕获的 lambda 语法和其他什么,它就会更自然。Bind 需要"从内到外"阅读它,在脑海中解析永远不会变得自然,对于成员函数和捕获引用来说甚至更加乏味。考虑(改编自 Boost 文档(:

struct S
{
void foo( int&, double ) const;
} s;
// ...
auto x      = 0;
auto binder = bind( &S::foo, s, ref(x), placeholder::_1 ); // s.foo(x-as-ref, _1)
auto lambda = [&]( auto d ) { s.foo( x, d ); };
auto d = 42.0;
binder( d );
lambda( d );

底部的调用语法是相同的,但 lambda 定义对实际发生的事情更加清晰。Lambda 也很容易扩展到多个语句,而 bind 则不会。