c++ boost::asio thread pool.
thread_pool(num_threads); thread_pool(num_threads, initial_services);
attach / executor / get_executor / join / notify_fork / stop / wait
destroy / shutdown
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.
Obtains the executor associated with the pool.
pool.executor(); // return executor_type pool.get_executor(); // return executor_type
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
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
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
:) 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.
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
Thu Nov 20 04:56:59 AM UTC 2025
//////////////////////////////////////////////////////////////////////
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