0

私は最近、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 のインスタンスと、io_service_.run() を呼び出す std::thread のプールを作成しました。startAccept を呼び出した後、メイン スレッドはコマンド ライン入力を待つだけです。

スレッド プール内のスレッドの 1 つが、接続の開始時に 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|socket@00A2F710.async_accept

含めた 2 番目のコード ブロックを実行してサーバーに接続すると、次の出力が得られます。

@asio|1350656734.789896|0*1|io_service@00ECEC78.post
@asio|1350656734.789896|>1|
@asio|1350656734.789896|1*2|socket@00D0FDE0.async_accept
@asio|1350656734.789896|<1|
@asio|1350656756.150051|>2|ec=system:0
@asio|1350656756.150051|2*3|io_service@00ECEC78.post
@asio|1350656756.150051|>3|
@asio|1350656756.150051|2*4|socket@00EE9090.async_send
@asio|1350656756.150051|3*5|socket@00D0FDE0.async_accept
@asio|1350656756.150051|2*6|socket@00EE9090.async_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(); })));
}
4

2 に答える 2

0

io_service.run関数は実際のイベントループであり、基本的にはループで呼び出すことですio_service.post

編集:io_service.postドキュメントから:

指定されたハンドラーを呼び出してすぐに戻るようにio_serviceに要求します。

ハンドラーが、、、メンバー関数が現在呼び出されているスレッドでのみ呼び出されるio_serviceことを保証します。run()run_one()poll() or poll_one()

行うべきことは、独自のイベントループを実装するか、を呼び出すio_service.run_oneio_service.run、Boostにイベントループを処理させるために呼び出すことです。また、イベントループを実行するスレッドは関係ありません。すべてのイベントハンドラーは、イベントループを実行するスレッドから呼び出されます。

于 2012-10-19T05:30:01.777 に答える
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;
}
于 2012-10-19T08:27:23.810 に答える