asio boost 异步错误处理_asio异步化的实现方法

我们有时需要一些自己把一线阻塞操作在asio中去调用,如何将阻塞和asio的异步结合,又get_associated_executor在什么时候可能用到,这里直接上代码:

template<class Handler>
BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(boost::system::error_code, result_type))
async_do_something(Handler&& handler)
{
	// ASYNC_HANDLER_TYPE_CHECK(Handler, void(boost::system::error_code, result_type));
	boost::asio::async_completion<Handler, void(boost::system::error_code, result_type)> init(handler);

	boost::async(boost::launch::async, [handler = init.completion_handler]() mutable
	{
		boost::system::error_code ec;
		result_type result;

		// 执行操作,并拿到result或ec...
		// ...

		auto executor = boost::asio::get_associated_executor(handler);
		boost::asio::dispatch(executor, [ec, handler, result]() mutable
		{
			handler(ec, result);
		});
	});

	return init.result.get();
}

在这个代码中

BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(boost::system::error_code, result_type))

第2个参数void(boost::system::error_code, result_type),第一个是error_code,第二个是async_do_something推导的返回类型,如果handler是callback,则会推导async_dosomething返回为void,如果是boost::asio::yield_context,则会推导为result_type类型。

最终要做的事,是在boost::async中完成的,这里boost::async可以替换成std::thread之类的线程函数也是一样,也就是说,真正执行的操作是在独立的线程中完成的。(当然这里是为了说明阻塞工作执行在另一个线程中,使用了线程来说明,也可以是其它异步操作并非需要开线程,但流程是一样)

当这个线程执行完真正的操作后

auto executor = boost::asio::get_associated_executor(handler);

这是拿到handler关联的执行器上下文,再通过boost::asio::dispatch投递到executor上执行handler,实现线程切回调用者线程,这在协程中非常有必要。

例如, 我们最终使用这段代码是这样的:

boost::system:error_code ec; // 假设线程id是1
auto result = async_do_something(yield[ec]); // 那么假设在这个内部boost::async去执行实际操作的线程是2,async_do_something执行完成返回时,又将切回线程id是1
std::cout << result; // 输出async_do_something返回的结果,执行到这里就是在线程1上执行的了

如果没有auto executor = boost::asio::get_associated_executor(handler);我们不能保证线程切回 1

这篇文章大致讲了boost::asio::get_associated_executor的用途,也算是上篇文章的补充吧,实际上,如果考虑内存分配一至性,同样有associated_allocator,这里不作展开。

上面async_do_something的实现不兼容c++20协程方案,若要支持的话,代码需要变化一下,稍为麻烦一点,具体做法是:

struct initiate_do_something
{
	template <typename Handler>
	void operator()(Handler&& handler) const
	{
		boost::asio::detail::non_const_lvalue<Handler> handler2(handler);

		boost::async(boost::launch::async, [handler = std::move(handler2.value)]() mutable
		{
			boost::system::error_code ec;
			int result = 0x47;

			// 执行真正操作,并拿到result或ec...
			// ...

			auto executor = boost::asio::get_associated_executor(handler);
			boost::asio::dispatch(executor, [ec, handler = std::move(handler), result]() mutable
			{
				handler(ec, result);
			});
		});
	}
};

template<class Handler>
BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(boost::system::error_code, int))
async_do_something(Handler&& handler)
{
	return boost::asio::async_initiate<Handler,
		void(boost::system::error_code, int)>(initiate_do_something(), handler);
}

这样,我们就可以像asio其它方法一样,使用c++20协程方案了,如:

awaitable<void> do_something()
{
	int ret = co_await async_do_something(use_awaitable);
	std::cout << ret << std::endl;
}

// 在某处启动协程
co_spawn(ioc, do_something, detached);

本文首发于:https://bbs.avplayer.org/t/topic/1883

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Boost.Asio 是一个功能强大的 C++ 库,提供了对网络和底层 I/O 操作的跨平台支持。它可以用于实现 TCP 异步通信。 要使用 Boost.Asio 进行 TCP 异步通信,首先需要创建一个 io_context 对象,它负责处理底层的 I/O 操作。然后,创建一个 tcp::socket 对象,用于建立和管理 TCP 连接。接下来,使用异步操作来进行连接、读取和写入数据。 下面是一个简单的示例代码,展示了如何使用 Boost.Asio 进行 TCP 异步通信: ```cpp #include <iostream> #include <boost/asio.hpp> using boost::asio::ip::tcp; class TcpClient { public: TcpClient(boost::asio::io_context& io_context) : io_context_(io_context), socket_(io_context) {} void Connect(const std::string& host, const std::string& port) { tcp::resolver resolver(io_context_); tcp::resolver::results_type endpoints = resolver.resolve(host, port); boost::asio::async_connect(socket_, endpoints, [this](boost::system::error_code ec, tcp::endpoint endpoint) { if (!ec) { std::cout << "Connected to server: " << endpoint << std::endl; Read(); } else { std::cout << "Failed to connect: " << ec.message() << std::endl; } }); } void Read() { boost::asio::async_read(socket_, boost::asio::buffer(data_, max_length), [this](boost::system::error_code ec, std::size_t length) { if (!ec) { std::cout << "Received data: " << std::string(data_, length) << std::endl; Read(); } else { std::cout << "Failed to read data: " << ec.message() << std::endl; } }); } void Write(const std::string& message) { boost::asio::async_write(socket_, boost::asio::buffer(message), [this](boost::system::error_code ec, std::size_t /*length*/) { if (ec) { std::cout << "Failed to write data: " << ec.message() << std::endl; } }); } private: boost::asio::io_context& io_context_; tcp::socket socket_; enum { max_length = 1024 }; char data_[max_length]; }; int main() { boost::asio::io_context io_context; TcpClient client(io_context); client.Connect("localhost", "1234"); // 发送数据示例 client.Write("Hello, server!"); io_context.run(); return 0; } ``` 在上述示例代码,`TcpClient` 类负责连接服务器、读取和写入数据。`Connect` 方法通过异步连接操作连接到指定的主机和端口,`Read` 方法通过异步读取操作接收服务器发送的数据,`Write` 方法通过异步写入操作向服务器发送数据。 你可以根据自己的需求修改以上示例代码,以适应你的具体应用场景。希望对你有帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值