В рамках работ над новой версией so_5_extra разрабатывается asio_thread_pool-диспетчер. Это диспетчер, в котором на пуле потоков запускаются методы asio::io_service::run(), и агенты, которые привязаны к такому диспетчеру, отрабатывают свои события на этих же рабочих потоках. По сути, диспетчеризацией событий для таких агентов занимается asio::io_service. Что должно дать возможность агентам просто и прозрачно выполнять и IO-операции, и обработку прикладных сообщений не задумываясь о том контексте, на котором они работают (ноги у этого всего растут вот отсюда).
Но самое интересное для меня другое: это был еще и эксперимент по созданию диспетчера, который может использовать не только std::thread для создания рабочих потоков, но и предоставленный пользователем класс нити, если этот класс частично мимикрирует под std::thread. Зачем такое может потребоваться?
Ну, например, если вы хотите использовать какую-то специфическую для ОС функциональность, недоступную через std::thread. Скажем, если вам нужно назначить собственные pthread_attr_t перед запуском рабочей нити. Это не такая уж и экзотическая ситуация. Например, если вы создаете большое количество нитей в приложении, то можете захотеть уменьшить размер стека для них, т.к. размер по умолчанию для вашей задачи может быть слишком большой.
Так вот, этот новый asio_thread_pool показал, что подобный фокус вполне себе работает. И выглядит это как-то вот так:
// Definition of traits to be used with asio_thread_pool. struct my_disp_traits { // Actual type of thread to be used. using thread_type = my_pthread_t; }; ... // Create dispatcher for ring of agents. auto disp = asio_tp::create_private_disp<my_disp_traits>( coop.environment(), "asio_tp", std::move(disp_params) ); |
Стоить определить в traits-ах для диспетчера имя класса, который следует использовать вместо std::thread, и asio_thread_pool начнет использовать данный класс.
Любопытные последствия могут быть у этого эксперимента. У меня уже давно есть мысль о том, чтобы добавить возможность такой кастомизации ко всем штатным диспетчерам SO-5. И, поскольку эксперимент оказался удачным, такая возможность может воплотиться в реальность в SO-5.5.20, работа над которой должна начаться вот прямо после фиксации so_5_extra-1.0.2. А это означает, что реализации штатных диспетчеров вынужденно станут шаблонными. Что заметно уменьшит количество .cpp-файлов в исходниках SObjectizer-а. Что будет означать еще одним большой шаг в сторону header-only версии SO-5. При этом header-only -- это вовсе не самоцель. Но чем больше будет в SO-5 кастомизаций через шаблоны, тем ближе к header-only окажется реализация.
Под катом простейшая реализация my_pthread_t, набросанная на коленке за 15 минут...
class my_pthread_t { struct data_t { pthread_t m_thread; std::function<void()> m_func; data_t( std::function<void()> func ) : m_func( std::move(func) ) {} void join() noexcept { pthread_join( m_thread, nullptr ); } }; std::unique_ptr<data_t> m_impl; static void * thread_body( void * arg ) { auto * impl = reinterpret_cast<data_t*>(arg); impl->m_func(); return nullptr; } public : my_pthread_t() = default; template<typename F> my_pthread_t( F && f ) : m_impl( std::make_unique< data_t >( std::move(f) ) ) { const auto rc = pthread_create( &(m_impl->m_thread), nullptr, &my_pthread_t::thread_body, m_impl.get() ); if( 0 != rc ) throw std::system_error( std::error_code(errno, std::generic_category()) ); } my_pthread_t( const my_pthread_t & ) = delete; my_pthread_t( my_pthread_t && other ) noexcept : m_impl( std::move(other.m_impl) ) {} ~my_pthread_t() { join(); } friend void swap( my_pthread_t & a, my_pthread_t & b ) noexcept { using std::swap; swap( a.m_impl, b.m_impl ); } my_pthread_t & operator=( my_pthread_t && other ) noexcept { my_pthread_t tmp( std::move(other) ); swap( *this, tmp ); return *this; } void join() noexcept { if( m_impl ) { auto impl = std::move(m_impl); impl->join(); } } }; |
Комментариев нет:
Отправить комментарий