简体   繁体   中英

Boost ASIO blocking callbacks

Is it safe (or reasonable) for a the callback from asio to block?

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(...);
}

Or should I spawn a thread in callback, then return immediately and do operations when that thread is done?

Yes, it is reasonable in many scenarios. But you as the architect are responsible for deciding if it is actually reasonable in your scenario.

Generally speaking, your io_service will run on one thread, and that is the thread it will invoke your callback on, which means it is not processing messages while it is invoking the callback. If that's okay, then you're good to go. (Note that the "safe" part comes up here -- are you properly protecting your objects for use across threads?)

If it's not okay, then you should ship off the message to another thread for processing.

As for me and my house, we prefer the Active Object pattern to keep the IO service responsive and the multithreading worries to minimum. See Herb Sutter's article series on Effective Concurrency . Specifically, you should read:

PS, If at all possible, drop boost::bind() in favor of C++11/14 lambdas.


Update : As for why to prefer lambdas, the answer boils down to clarity and maintainability (performance is about the same , with a slight edge for lambdas). Once you are used to the lambda syntax for capture and what not, it is much more natural. Bind requires reading it "inside out", never becomes natural to parse mentally, and is even more tedious for member functions and capturing references. Consider (adapted from the Boost docs ):

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 );

The call syntax at the bottom is same, but the lambda definition is much clearer as to what's actually happening. Lambdas also easily expand to multiple statements, whereas bind does not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM