首页 > 解决方案 > io_context::strand 可以保证 async_* 完成处理程序和自定义函子之间的顺序吗?

问题描述

我有这种情况,我需要在调用一个自定义函数之前“准备”一个 async_read 操作以进行读取,该函数在 websocket 的对应部分发送一些东西。我将 async_read 的完成处理程序包装在我发布仿函数的同一链中。

问题是有时 on::read 没有被调用,所以基本上我认为 writeFromCounterpart() 在 ws_1 处于“读取”状态之前被调用。

我的理解是 strand 保证了完成处理程序之间的顺序,但我不明白是否保证 async_* 操作在继续从 strand 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;
}

标签: boost-asioboost-beast

解决方案


简短的回答是:的。

它的文档在这里:

问题是,有时当我运行此流程时,不会调用 on::read,所以基本上我认为 SendSomethingOnTheWs 在 ws_ 处于“读取”状态之前被调用。

这有什么关系?流式套接字(ws 或其他)的行为类似于流。如果套接字打开,则数据将被缓冲。


推荐阅读