提升ASIO:异步写入,同步处理

6

我有一个程序(客户端+服务器),这个写法能正常工作,没有任何问题:

boost::asio::write(this->socket_, boost::asio::buffer(message.substr(count,length_to_send)));

其中socket_boost::asio::ssl::stream<boost::asio::ip::tcp::socket>message是一个std::string

我希望让它更好,并且非阻塞,所以我创建了一个可以替换它的函数,调用方式如下:

write_async_sync(socket_,message.substr(count,length_to_send));

这个函数的目的是:
  1. 内在地使调用异步化
  2. 保持接口不变
我实现的这个函数简单使用promise/future来模拟同步行为,之后(当它起作用后)我将修改它以支持取消操作。
std::size_t 
SSLClient::write_async_sync(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& socket, 
                            const std::string& message_to_send)
{
    boost::system::error_code write_error;
    std::promise<std::size_t> write_promise;
    auto write_future = write_promise.get_future();

    boost::asio::async_write(socket,
                             boost::asio::buffer(message_to_send), 
        [this,&write_promise,&write_error,&message_to_send]
        (const boost::system::error_code& error,
        std::size_t size_written)
        {
            logger.write("HANDLING WRITING");
            if(!error)
            {
                write_error = error;
                write_promise.set_value(size_written);
            }
            else
            {
                write_promise.set_exception(std::make_exception_ptr(std::runtime_error(error.message())));
            }
        });
    std::size_t size_written = write_future.get();
    return size_written;
}

问题:我无法让异步功能正常工作。同步功能正常,但异步却会冻结并且永远不会进入lambda部分(写入从未发生)。我做错了什么?
编辑:我意识到使用poll_one()会使函数执行并继续进行,但我不理解它。这是我为io_service调用run()的方式(在启动客户端之前):
io_service_work = std::make_shared<boost::asio::io_service::work>(io_service);
io_service_thread.reset(new std::thread([this](){io_service.run();}));

基本上这些是 shared_ptr。这样做有问题吗?这种方式是否需要使用 poll_one()


你是否有一个线程调用了 io_service.run() 函数? - florgeng
@florgeng 当然。使用同步调用write时,齿轮运转良好。但异步调用则存在问题。在构造函数中将io_service传递给socket_ - Sam Markus
@florgeng,请检查我的更新 :) - Sam Markus
1个回答

2

关于编辑:

您已经正确使用了io_service::run()。这告诉我您正在一个(完成)处理程序内阻塞 future。显然,这会阻止run()推进事件循环。


@florgeng所问的问题并不是您是否有一个io_service实例。

问题是您是否适当地调用了run()(或poll()),以便异步操作可以继续进行。

此外,您已经可以使用内置的future<>

std::future<std::size_t> recv_length = socket.async_receive_from(
      boost::asio::buffer(recv_buf),
      sender_endpoint,
      boost::asio::use_future);

哦,谢谢你提供的未来提示。太棒了!虽然我正在使用std::thread调用运行,但我发现调用poll_one()会使其继续执行。我不明白为什么会这样。请检查我的编辑。如果有明显的原因,请解释一下,我将不胜感激。 - Sam Markus
是的,根据proactor模型的文档设计(底部的ad.5)和关于线程的说明(链接在此),这是显而易见的。第一个异步教程程序中提到:“最后,我们必须在io_service对象上调用io_service::run()成员函数。”(链接在此 - sehe
啊,发现了修改。已经修正了答案。另请参阅https://dev59.com/I2Ml5IYBdhLWcg3wsIuA。 - sehe
我意识到处理程序被检查 std::this_thread::get_id() 阻塞了。谢谢提示。 - Sam Markus
为了澄清未来的任何普通访客:Sam 的意思是他通过查看线程 ID 来发现它。this_thread::get_id() 不会阻塞 :) - sehe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接