io_context::strand 能否保证 async_* 完成处理程序和自定义仿函数之间的顺序?
Can a io_context::strand guarantee the order between an async_* completion handler and custom functor?
我有这种情况,在我调用一个自定义函数之前我需要有一个 async_read 操作 "prepared" 来读取,该函数在 websocket 的对应部分发送一些东西。我将 async_read 的完成处理程序包裹在与我 post 我的仿函数相同的链中。
问题是有时 on::read 没有被调用,所以基本上我认为 writeFromCounterpart() 在 ws_1 处于 "reading" 状态之前被调用。
我的理解是 strand 保证完成处理程序之间的顺序,但我不明白是否保证 async_* 操作是 "ready"(运行 在其线程和读取之前)继续从链 FIFO 进行其他操作。
完整代码如下:当 运行 时,大多数时候我看到以下输出:
on_read called
Run 2 Handlers
我也多次看到以下内容(在压力条件下大约每 20 人就有 1 次),
on_write called
Run 1 Handlers
我从未见过 on_write 和 on_read
代码:
#include <boost/asio.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/core.hpp>
#include <string>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
namespace websocket = boost::beast::websocket; // from <boost/beast/websocket.hpp>
boost::beast::multi_buffer buffer;
boost::asio::io_context ioc_1;
boost::asio::io_context ioc_2;
websocket::stream<tcp::socket> ws_1(ioc_1);
websocket::stream<tcp::socket> ws_2(ioc_2);
void on_write(boost::system::error_code, std::size_t) {
std::cout << "on_write called" << std::endl << std::flush;
}
void writeFromCounterpart() {
ws_2.async_write(boost::asio::buffer(std::string("Hello")), on_write);
}
void on_read(boost::system::error_code, std::size_t) {
std::cout << "on_read called" <<std::endl << std::flush;
}
int main() {
std::thread t([](){
auto const address = boost::asio::ip::make_address("127.0.0.1");
tcp::acceptor acceptor{ioc_2, {address, 30000}};
tcp::socket socket{ioc_2};
acceptor.accept(socket);
websocket::stream<tcp::socket> ws{std::move(socket)};
ws.accept();
ws_2 = std::move(ws);
ioc_2.run();
});
t.detach();
// allow acceptor to accept
std::this_thread::sleep_for(std::chrono::milliseconds(200));
tcp::resolver resolver_(ioc_1);
boost::asio::io_context::strand strand_(ioc_1);
auto const results = resolver_.resolve("127.0.0.1", "30000");
boost::asio::connect(ws_1.next_layer(), results.begin(), results.end());
ws_1.handshake("127.0.0.1", "/");
ws_1.async_read(buffer, strand_.wrap(on_read));
strand_.post(writeFromCounterpart);
auto handlers = ioc_1.run_for(std::chrono::milliseconds(5000));
std::cout << "Run " + std::to_string(handlers) + " Handlers" << std::endl << std::flush;
}
简短的回答是:是。
它的文档在这里:
The problem is that sometimes when I run this flow, on::read is not called, so basically I think that the SendSomethingOnTheWs is called before the ws_ was in "reading" state.
为什么这很重要?流式套接字(ws 或其他)的行为类似于流。如果套接字打开,数据将被缓冲。
我有这种情况,在我调用一个自定义函数之前我需要有一个 async_read 操作 "prepared" 来读取,该函数在 websocket 的对应部分发送一些东西。我将 async_read 的完成处理程序包裹在与我 post 我的仿函数相同的链中。
问题是有时 on::read 没有被调用,所以基本上我认为 writeFromCounterpart() 在 ws_1 处于 "reading" 状态之前被调用。
我的理解是 strand 保证完成处理程序之间的顺序,但我不明白是否保证 async_* 操作是 "ready"(运行 在其线程和读取之前)继续从链 FIFO 进行其他操作。
完整代码如下:当 运行 时,大多数时候我看到以下输出:
on_read called
Run 2 Handlers
我也多次看到以下内容(在压力条件下大约每 20 人就有 1 次),
on_write called
Run 1 Handlers
我从未见过 on_write 和 on_read
代码:
#include <boost/asio.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/core.hpp>
#include <string>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
namespace websocket = boost::beast::websocket; // from <boost/beast/websocket.hpp>
boost::beast::multi_buffer buffer;
boost::asio::io_context ioc_1;
boost::asio::io_context ioc_2;
websocket::stream<tcp::socket> ws_1(ioc_1);
websocket::stream<tcp::socket> ws_2(ioc_2);
void on_write(boost::system::error_code, std::size_t) {
std::cout << "on_write called" << std::endl << std::flush;
}
void writeFromCounterpart() {
ws_2.async_write(boost::asio::buffer(std::string("Hello")), on_write);
}
void on_read(boost::system::error_code, std::size_t) {
std::cout << "on_read called" <<std::endl << std::flush;
}
int main() {
std::thread t([](){
auto const address = boost::asio::ip::make_address("127.0.0.1");
tcp::acceptor acceptor{ioc_2, {address, 30000}};
tcp::socket socket{ioc_2};
acceptor.accept(socket);
websocket::stream<tcp::socket> ws{std::move(socket)};
ws.accept();
ws_2 = std::move(ws);
ioc_2.run();
});
t.detach();
// allow acceptor to accept
std::this_thread::sleep_for(std::chrono::milliseconds(200));
tcp::resolver resolver_(ioc_1);
boost::asio::io_context::strand strand_(ioc_1);
auto const results = resolver_.resolve("127.0.0.1", "30000");
boost::asio::connect(ws_1.next_layer(), results.begin(), results.end());
ws_1.handshake("127.0.0.1", "/");
ws_1.async_read(buffer, strand_.wrap(on_read));
strand_.post(writeFromCounterpart);
auto handlers = ioc_1.run_for(std::chrono::milliseconds(5000));
std::cout << "Run " + std::to_string(handlers) + " Handlers" << std::endl << std::flush;
}
简短的回答是:是。
它的文档在这里:
The problem is that sometimes when I run this flow, on::read is not called, so basically I think that the SendSomethingOnTheWs is called before the ws_ was in "reading" state.
为什么这很重要?流式套接字(ws 或其他)的行为类似于流。如果套接字打开,数据将被缓冲。