2012-10-19 50 views
0

我最近一直在使用boost asio和一些新的C++ 11結構。以下是導致意外行爲的代碼示例部分(至少對我而言)。asio .async_ *將不會運行,除非主線程調用io_service.run

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

我撥打電話到服務器:: startAccept之前,我創建io_service對象::工作的一個實例,)的std ::線程池,要求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]EC78.post 
@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(); }))); 
} 
+1

注意,它並沒有成爲_main_線程,它可以是任何線程。實際上它可能是_多個線程。 –

+0

@ K-ballo我已經從另一個不是主線程的線程調用了io_service.run。只有在我提供的第一個代碼塊中的主線程調用io_service.run時,纔會執行Server :: accept_handler。 – flumpb

+1

最初的代碼和描述看起來很好,並且這種行爲通常會來自[work](http://www.boost.org/doc/libs/1_51_0/doc/html/booster_asio/reference/io_service__work.html)對象超出範圍。但是,它沒有解釋後一種行爲。也許啓用[BOOST_ASIO_ENABLE_HANDLER_TRACKING](http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/overview/core/handler_tracking.html)將提供一些見解。 –

回答

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讓加速處理事件循環。 其中線程用於運行事件循環,所有事件處理程序將從您運行事件循環的線程中調用。

+0

但爲什麼我必須運行我提供的第二塊代碼才能使accept_handler由不是主線程的線程執行? – flumpb

+0

@kisplit請看我更新的答案。 –

+0

@JoachimPileborg據我所知,問題在於,async_accept不會在第一種情況下在io_service :: run線程中執行 – pogorskiy

相關問題