1. 程式人生 > 其它 >asio boost 非同步錯誤處理_asio非同步化的實現方法

asio boost 非同步錯誤處理_asio非同步化的實現方法

技術標籤:asio boost 非同步錯誤處理

我們有時需要一些自己把一線阻塞操作在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