asio .async_ * не будет работать, если основной поток не вызовет io_service.run

Недавно я играл с boost asio и некоторыми новыми конструкциями c ++ 11. Вот пример раздела кода, который вызывает неожиданное поведение (по крайней мере, для меня).

void Server::startAccept()
{
connections_.push_back(std::make_shared<Connection>(io_service_));
acceptor_.async_accept(connections_.back()->socket(), std::bind(&Server::accept_handler, this, connections_.back(), std::placeholders::_1));
}

void Server::accept_handler(std::shared_ptr<Connection> con, const boost::system::error_code& ec)
{
startAccept();

if (!ec) {
con->start();
}
}

Прежде чем я вызову Server :: startAccept, я создал экземпляр io_service :: work и пул std :: thread, который вызвал io_service_.run (). После того, как я сделаю вызов startAccept, основной поток просто ждет ввода из командной строки.

Я ожидаю, что один из потоков в моем пуле потоков запустит Server :: accept_handler при установлении соединения. Этого не происходит Вместо этого я должен вызвать io_service_.run () из основного потока.

Теперь я немного поиграл и обнаружил, что могу добиться желаемого поведения, выполнив следующее:

void Server::startAccept()
{
connections_.push_back(std::make_shared<Connection>(io_service_));
io_service_.post([this]() { acceptor_.async_accept(connections_.back()->socket(), std::bind(&Server::accept_handler, this, connections_.back(), std::placeholders::_1)); });
}

void Server::accept_handler(std::shared_ptr<Connection> con, const boost::system::error_code& ec)
{
startAccept();

if (!ec) {
con->start();
}
}

В чем разница между операциями .async_ * и io_service.post?

РЕДАКТИРОВАТЬ: Определение BOOST_ASIO_ENABLE_HANDLER_TRACKING

Когда я компилирую и запускаю свою программу, а затем подключаюсь к серверу с первым блоком кода, который я включил, это вывод:

@asio|1350656555.431359|0*1|[email protected]_accept

Когда я запускаю второй блок кода, который я включил, и подключаюсь к серверу, я получаю такой вывод:

@asio|1350656734.789896|0*1|[email protected]
@asio|1350656734.789896|>1|
@asio|1350656734.789896|1*2|[email protected]_accept
@asio|1350656734.789896|<1|
@asio|1350656756.150051|>2|ec=system:0
@asio|1350656756.150051|2*3|[email protected]
@asio|1350656756.150051|>3|
@asio|1350656756.150051|2*4|[email protected]_send
@asio|1350656756.150051|3*5|[email protected]_accept
@asio|1350656756.150051|2*6|[email protected]_receive
@asio|1350656756.150051|<3|
@asio|1350656756.150051|>4|ec=system:0,bytes_transferred=54
@asio|1350656756.150051|<2|
@asio|1350656756.150051|<4|
@asio|1350656758.790803|>6|ec=system:10054,bytes_transferred=0
@asio|1350656758.790803|<6|

РЕДАКТИРОВАТЬ 2: понимание создания темы

for (int i = 0; i < NUM_WORKERS; i++) {
thread_pool.push_back(std::shared_ptr<std::thread>(new std::thread([this]() { io_service_.run(); })));
}

0

Решение

Если вы не забыли вызвать io_service :: run для каждого потока в пуле и использовали io_service :: work, чтобы избежать выхода из цикла io_service :: run, ваш код в первом случае абсолютно корректен.

Вот рабочий пример (я игнорирую обработку соединения и корректное завершение программы)

class Connection
{
public:
Connection(boost::asio::io_service & io_serivce) : socket_(io_serivce) {}
boost::asio::ip::tcp::socket & socket() { return socket_; }
private:
boost::asio::ip::tcp::socket socket_;
};

class Server
{
public:
Server(boost::asio::io_service & io_serivce, const std::string & addr, const std::string & port) : io_service_(io_serivce), acceptor_(io_service_) {
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(addr, port);
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
acceptor_.open(endpoint.protocol());
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
acceptor_.bind(endpoint);
acceptor_.listen();
startAccept();
}

void startAccept(){
connections_.push_back(boost::make_shared<Connection>(  boost::ref(io_service_) ));
acceptor_.async_accept(connections_.back()->socket(), boost::bind(&Server::accept_handler, this, connections_.back(), _1) );
}

void Server::accept_handler(boost::shared_ptr<Connection> con, const boost::system::error_code& ec)
{
std::cout << "start connection" << std::endl;
startAccept();
}

private:
boost::asio::io_service & io_service_;
boost::asio::ip::tcp::acceptor acceptor_;
std::vector< boost::shared_ptr<Connection> > connections_;
};

int main(int argc, char * argv[])
{
// thread pool
boost::thread_group threads_;
boost::asio::io_service io_service_;
boost::asio::io_service::work work_(io_service_);

const size_t kThreadsCount = 3;
for (std::size_t i = 0; i < kThreadsCount; ++i) {
threads_.create_thread(boost::bind(&boost::asio::io_service::run, &io_service_));
}

Server s(io_service_, "127.0.0.1", "8089");

char ch;
std::cin >> ch;
return 0;
}
0

Другие решения

io_service.run Функция является фактическим циклом событий, и в основном она вызывает io_service.post в петле.

Редактировать: От io_service.post документация:

Запросите io_service для вызова данного обработчика и немедленного возврата.

А также

io_service гарантирует, что обработчик будет вызываться только в потоке, в котором run(), run_one(), poll() or poll_one() функции-члены в настоящее время вызывается.

Что ты должен сделать это либо реализовать свой собственный цикл событий, вызывая io_service.run_one, или позвоните по телефону io_service.run позволить Boost обрабатывать цикл событий. И это не имеет значения который В потоке, из которого вы запускаете цикл событий, все обработчики событий будут вызываться из потока, в котором вы запускаете цикл событий.

0

По вопросам рекламы [email protected]