【问题标题】:Confused about boost::asio::yield order of execution in same strand对同一链中的 boost::asio::yield 执行顺序感到困惑
【发布时间】:2016-11-27 11:18:15
【问题描述】:

我正在尝试使用 asio 编写一个客户端,它执行以下操作:

  1. 连接到服务器。
  2. 尝试在连接到服务器后读回一些数据。

我发现的问题是操作似乎不像我所期望的那样按顺序执行。这是代码:

std::future<NetMessage> Cliente::asyncConnectTo(std::string const & hostname,
                                          int port,
                                          std::string const & name) {
    using namespace std::literals;
    boost::asio::spawn
        (strand_,
         [this, name, port, hostname](boost::asio::yield_context yield) mutable {
            i_->playerLog->trace() << name << " connecting to " << hostname << ':'
                                   << port;
            Guinyote::Utils::connectWith
                (*this, std::move(hostname), port,
                 std::move(name), yield);

            i_->playerLog->info() << "Connected to server.";
        });
    runthread_ = std::thread([&] {
            try {
                i_->playerLog->info() << "Starting...";
                this->service_.run();
            }
            catch (std::exception & e) {
                std::cout << e.what() << std::endl;
            }
        });
    return this->asyncReceiveMessage(); //This function spawns another coroutine through the same strand_ object.
}

函数this-&gt;asyncReceiveMessage()预计会收到服务器连接后发回的消息:

std::future<NetMessage> Cliente::asyncReceiveMessage() {
    namespace ba = boost::asio;

    std::promise<NetMessage> prom;
    std::future<NetMessage> message = prom.get_future();
    ba::spawn
        (strand_,
         [this, p = std::move(prom)](boost::asio::yield_context yield) mutable {
            i_->playerLog->trace("waiting to read message from server socket...");
            boost::system::error_code ec{};
            boost::int64_t messageSize{};
            ba::async_read(
                socket_,
                ba::buffer(&messageSize, sizeof(boost::int64_t)),
                yield);

            i_->playerLog->trace() << "Client: Received message of "
                                   << messageSize << " bytes. Reading message...";

            std::vector<char> serverMessageData(messageSize);
            ba::async_read
                (socket_,
                 ba::buffer(serverMessageData),
                 yield);

            using namespace boost::iostreams;
            basic_array_source<char> input_source(&serverMessageData[0], serverMessageData.size());
            stream<basic_array_source<char>> stream(input_source);
            boost::archive::binary_iarchive archive(stream);
            Utils::MensajeRed msg;

            archive >> msg;
            i_->playerLog->trace() << "NetMessage correctly read.";
            p.set_value(std::move(msg));

        });
    return message;
}

在我的日志文件中,我在客户端得到以下信息:

[clientLog] [info] Client: Starting...
[clientLog] [trace] User1234 connecting to localhost:10004
[clientLog] [trace] waiting to read message from server socket...

但我希望该日志的第三行会出现在[clientLog] [info] Connected to server. 之后所以我的期望如下:

[clientLog] [info] Client: Starting...
[clientLog] [trace] User1234 connecting to localhost:10004
[clientLog] [info] Connected to server.
[clientLog] [trace] waiting to read message from server socket...

也就是说,“连接到服务器”应该总是发生在之前“等待从服务器套接字读取消息...”。

有人知道发生了什么吗?我以为strand_ 会保证执行顺序,但似乎我可能误解了一些东西。获得我想要的效果的正确解决方案是什么?

【问题讨论】:

标签: c++ boost network-programming boost-asio


【解决方案1】:

所以这里的问题是任务将按顺序启动,但后续boost::asio::spawn链中的spawn调用并不意味着第一个任务将在第二个之前完成。

这只是第一个任务在第二个任务之前开始,没有别的。

为了保持顺序,我只是创建了一个在 asyncConnectTo 的 spawn 内部调用的协程,而不是生成两个不同的协程。这样我可以确保第一个协程在第二个协程之前完成:

NetMessage Cliente::asyncReceiveMessageCoro(boost::asio::yield_context yield) {
    namespace ba = boost::asio;
    i_->playerLog->trace("waiting to read message from server socket...");
    boost::system::error_code ec{};
    boost::int64_t messageSize{};
    ba::async_read(
      socket_,
      ba::buffer(&messageSize, sizeof(boost::int64_t)),
      yield);

    i_->playerLog->trace() << "Client: Received message of "
                                   << messageSize << " bytes. Reading message...";

    std::vector<char> serverMessageData(messageSize);
    ba::async_read
       (socket_,
        ba::buffer(serverMessageData),
        yield);

    using namespace boost::iostreams;
    basic_array_source<char> input_source(&serverMessageData[0], serverMessageData.size());
    stream<basic_array_source<char>> stream(input_source);
    boost::archive::binary_iarchive archive(stream);
    Utils::MensajeRed msg;

    archive >> msg;
    i_->playerLog->trace() << "NetMessage correctly read.";
    return msg;
}

这个协程最后可以链接起来:

std::future<NetMessage> Cliente::asyncConnectTo(std::string const & hostname,
                                          int port,
                                          std::string const & name) {
    using namespace std::literals;

    std::promise<NetMessage> msgProm;
    auto msg = msgProm.get_future();
    boost::asio::spawn
        (strand_,
         [this, name, port, hostname, p = std::move(msgProm)](boost::asio::yield_context yield) mutable {
            i_->playerLog->trace() << name << " connecting to " << hostname << ':'
                                   << port;
            Guinyote::Utils::connectWith
                (*this, std::move(hostname), port,
                 std::move(name), yield);

            i_->playerLog->info() << "Connected to server.";
            p.set_value(this->asyncReceiveCoro(yield));
        });
    runthread_ = std::thread([&] {
            try {
                i_->playerLog->info() << "Starting...";
                this->service_.run();
            }
            catch (std::exception & e) {
                std::cout << e.what() << std::endl;
            }
        });
    return msg;
}

我的旧asyncReceiveMessage 只是spawn + 调用asyncReceiveMessageCoro 的组合。

【讨论】:

  • 感谢您抽出宝贵时间分享您的发现
猜你喜欢
  • 2019-10-16
  • 1970-01-01
  • 1970-01-01
  • 2015-12-18
  • 2017-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-08
相关资源
最近更新 更多