对同一链中的 boost::asio::yield 执行顺序感到困惑
Posted
技术标签:
【中文标题】对同一链中的 boost::asio::yield 执行顺序感到困惑【英文标题】:Confused about boost::asio::yield order of execution in same strand 【发布时间】:2016-11-27 11:18:15 【问题描述】:我正在尝试使用 asio 编写一个客户端,它执行以下操作:
-
连接到服务器。
尝试在连接到服务器后读回一些数据。
我发现的问题是操作似乎不像我所期望的那样按顺序执行。这是代码:
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->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_
会保证执行顺序,但似乎我可能误解了一些东西。获得我想要的效果的正确解决方案是什么?
【问题讨论】:
我看到这是一样的,但我没有看到合适的解决方案。它实际上只是说类似“手动处理”:***.com/questions/19946555/… 【参考方案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
的组合。
【讨论】:
感谢您抽出宝贵时间分享您的发现以上是关于对同一链中的 boost::asio::yield 执行顺序感到困惑的主要内容,如果未能解决你的问题,请参考以下文章