PrevUpHome

boost::asio::thread_pool, executor


boost::asio::thread_pool

c++ boost::asio thread pool.

Constructors

thread_pool(num_threads);
thread_pool(num_threads, initial_services);

Member functions

attach / executor / get_executor / join / notify_fork / stop / wait

Protected Member functions

destroy / shutdown

boost::asio executor contexts

boost::asio has some executor contexts:
asio::io_context,
asio::thread_pool,
asio::system_executor,
user-defined executors

Users can construct io objects such as sockets to use arbitrary executor types.

boost::asio executors provide a powerful abstraction for controlling where, when and how objects get executed.

Get executor from asio thread pool

Obtains the executor associated with the pool.

pool.executor();	// return executor_type
pool.get_executor();	// return executor_type

Post tasks to asio::thread_pool

Users can post tasks to asio::thread_pool object, its object will run tasks concurrently.

c++ example

#include <boost/asio.hpp>
#include <iostream>
#include <chrono>
#include <mutex>

int main()
{
	boost::asio::thread_pool pool{1024};
	std::mutex mutex;
	boost::asio::post(
		pool,
		[&mutex]
		{
			for (int i=0; i>-10; --i)
			{
				std::this_thread::sleep_for(std::chrono::milliseconds(100));
				std::unique_lock<std::mutex> lock{mutex};
				std::cout << i << ' ' << std::flush;
			}
		}
	);
	boost::asio::post(
		pool,
		[&mutex]
		{
			for (int i=0; i<10; ++i)
			{
				std::this_thread::sleep_for(std::chrono::milliseconds(100));
				std::unique_lock<std::mutex> lock{mutex};
				std::cout << i << ' ' << std::flush;
			}
		}
	);
	pool.join();
	std::cout << std::endl << "end" << std::endl;
}

output:

0 0 1 -1 -2 2 -3 3 -4 4 -5 5 -6 6 -7 7 -8 8 -9 9
end

Use asio::thread_pool object to replace asio::io_context for asio::co_spawn

c++ coroutines:

In boost::asio awaitable programming, the first parameter of asio::co_spawn is an executor, not only asio::io_context object;

And we can get the pool executor from asio::thread_pool object with method .executor() or .get_executor().

c++ example

#include <boost/asio.hpp>
#include <iostream>
#include <mutex>
#include <chrono>
#include <functional>

namespace use
{
	class resource:
		virtual public std::enable_shared_from_this<use::resource>
	{
	public:
		std::mutex mutex;
	};
	using shared_resource = std::shared_ptr<use::resource>;

	class docker:
		virtual public std::enable_shared_from_this<use::docker>
	{
	private:
		use::shared_resource __sres;
		std::uint16_t __port;
	public:
		docker(
			use::shared_resource sres__,
			std::uint16_t port__
		):
			__sres{sres__},
			__port{port__}
		{
		}
	public:
		boost::asio::awaitable<void> run()
		{
			for (int i=0; i<5; ++i)
			{
				std::this_thread::sleep_for(
					std::chrono::milliseconds(100));
				std::unique_lock<std::mutex> lock{__sres->mutex};
				std::cout << __port << " " << i << std::endl;
			}
			co_return;
		}
	};
}

int main()
{
	boost::asio::thread_pool pool{1024};
	auto sres = std::make_shared<use::resource>();
	for (std::uint16_t port=9000; port<9010; ++port)
	{
		boost::asio::co_spawn(
			pool.executor(),
			std::bind(
				&use::docker::run,
				std::make_shared<use::docker>(
					sres,
					port
				)
			),
			[] (std::exception_ptr eptr)
			{
				if (eptr)
				{
					std::cout << "/\\\n";
				}
			}
		);
	}
	pool.join();
}

output:

9000 0
9001 0
9007 0
9008 0
9004 0
9005 0
9003 0
9009 0
9006 0
9002 0
9000 1
9001 1
9007 1
9008 1
9004 1
9005 1
9003 1
9006 1
9009 1
9002 1
9000 2
9001 2
9008 2
9004 2
9007 2
9003 2
9006 2
9005 2
9009 2
9002 2
9000 3
9001 3
9004 3
9008 3
9006 3
9003 3
9005 3
9007 3
9002 3
9009 3
9000 4
9001 4
9008 4
9003 4
9005 4
9002 4
9006 4
9004 4
9007 4
9009 4

Nested asio::thread_pool and executor objects for asio::co_spawn

c++ example

#include <boost/asio.hpp>
#include <iostream>
#include <mutex>
#include <chrono>
#include <functional>
#include <boost/assert.hpp>

namespace use
{

	class meta_resource:
		virtual public std::enable_shared_from_this<use::meta_resource>
	{
	public:
		std::size_t loop_counter{0};
		std::mutex mutex{};
	};

	class resource:
		virtual public std::enable_shared_from_this<use::resource>
	{
	public:
		std::shared_ptr<use::meta_resource> meta;
	public:
		std::uint16_t port;
	public:
		resource(
			std::shared_ptr<use::meta_resource> meta,
			std::uint16_t port
		):
			meta{meta},
			port{port}
		{
		}
		virtual ~resource() = default;
	};
	using shared_resource = std::shared_ptr<use::resource>;

	class session:
		virtual public std::enable_shared_from_this<use::session>
	{
	private:
		use::shared_resource __sres;
		boost::asio::thread_pool __pool;
	public:
		session(
			use::shared_resource sres__
		):
			__sres{sres__},
			__pool{32}
		{
		}
		virtual ~session()
		{
			// Do not put join here.
			//__pool.join();		// line-A
		}
		boost::asio::awaitable<void> run()
		{
			for (int i=0; i<5; ++i)
			{
				boost::asio::co_spawn(
					__pool.executor(),
					std::bind(
						&use::session::loop,
						this->shared_from_this()
					),
					[] (std::exception_ptr eptr) {}
				);
			}
			// Put join here.
			__pool.join();		// line-B
			co_return;
		}
	private:
		boost::asio::awaitable<void> loop()
		{
			for (int i=0; i<5; ++i)
			{
				std::this_thread::sleep_for(std::chrono::milliseconds(50));		// line-C
				std::unique_lock<std::mutex> lock{__sres->meta->mutex};
				std::cout << i << ' ' << std::flush;
				++__sres->meta->loop_counter;
				lock.unlock();
			}
			co_return;
		}
	};

	class docker:
		virtual public std::enable_shared_from_this<use::docker>
	{
	private:
		use::shared_resource __sres;
	public:
		docker(
			use::shared_resource sres__
		):
			__sres{sres__}
		{
		}
	public:
		boost::asio::awaitable<void> run()
		{
			boost::asio::co_spawn(
				co_await boost::asio::this_coro::executor,
				std::bind(
					&use::session::run,
					std::make_shared<use::session>(
						__sres
					)
				),
				[] (std::exception_ptr eptr) {}
			);
			co_return;
		}
	};
}

int main()
{
	boost::asio::thread_pool pool{32};
	auto meta = std::make_shared<use::meta_resource>();
	for (std::uint16_t port=9000; port<9005; ++port)
	{
		auto sres = std::make_shared<use::resource>(meta, port);
		boost::asio::co_spawn(
			pool.executor(),
			std::bind(
				&use::docker::run,
				std::make_shared<use::docker>(
					sres
				)
			),
			[] (std::exception_ptr eptr)
			{
				if (eptr)
				{
					std::cout << "/\\\n";
				}
			}
		);
	}
	pool.join();		// line-D
	BOOST_ASSERT(meta->loop_counter == 125);
	std::cout << std::endl;
	std::cout << "loop counter: " << meta->loop_counter << std::endl;
	std::cout << std::endl << "end" << std::endl;
}

output:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
loop counter: 125

end

Analyze

:) If line-A is added and line-B is removed => (__pool.join(); at destructor

After the global pool.join(); at line-D, the local __pool.join(); might be still not called.

In such case, the sleeping of std::this_thread::sleep_for(std::chrono::milliseconds(50)); at line-C will let the loop-body code not be executed, then i's value will be not printed.

In such case, if remove line-C, loop-body of some partial threads will be executed, and some might be not executed, then the loop_counter's value will be incorrect, not 125 .

:)Do not remove line-B, and do not add line-A

So, to make sure local __pool.join(); is called, do not remove line-B and do not add line-A.

//////////////////////////////////////////////////////////////////////

Home

//////////////////////////////////////////////////////////////////////

Thu Nov 20 04:56:59 AM UTC 2025

//////////////////////////////////////////////////////////////////////

Cheers, cpp/c++ !

const std::string greeting = "Cheers, c++!";

std::cout << greeting << std::endl;

std::cout << greeting.data() << std::endl;

caught:

  ===================================
  #  The c++ programming language.  #
  #                                 #
  #  Join c++                       #
  #  Deck                           #
  ===================================

Powered by - B2 Build | boost quickbook

@cppfx.xyz


PrevUpHome