/*************************** @Author: Chunel @Contact: chunel@foxmail.com @File: UThreadPool.cpp @Time: 2022/10/3 17:31 @Desc: ***************************/ #include "UThreadPool.h" CGRAPH_NAMESPACE_BEGIN UThreadPool::UThreadPool(CBool autoInit, const UThreadPoolConfig& config) noexcept { cur_index_ = 0; is_init_ = false; this->setConfig(config); // setConfig 函数,用在 is_init_ 设定之后 if (autoInit) { this->init(); } } UThreadPool::~UThreadPool() { this->config_.monitor_enable_ = false; // 在析构的时候,才释放监控线程。先释放监控线程,再释放其他的线程 if (monitor_thread_.joinable()) { monitor_thread_.join(); } destroy(); } CStatus UThreadPool::setConfig(const UThreadPoolConfig &config) { CGRAPH_FUNCTION_BEGIN CGRAPH_ASSERT_INIT(false) // 初始化后,无法设置参数信息 this->config_ = config; CGRAPH_FUNCTION_END } CStatus UThreadPool::init() { CGRAPH_FUNCTION_BEGIN if (is_init_) { CGRAPH_FUNCTION_END } monitor_thread_ = std::move(std::thread(&UThreadPool::monitor, this)); thread_record_map_.clear(); task_queue_.setup(); primary_threads_.reserve(config_.default_thread_size_); for (int i = 0; i < config_.default_thread_size_; i++) { auto ptr = CGRAPH_SAFE_MALLOC_COBJECT(UThreadPrimary); // 创建核心线程数 ptr->setThreadPoolInfo(i, &task_queue_, &primary_threads_, &config_); // 记录线程和匹配id信息 thread_record_map_[(CSize)std::hash{}(ptr->thread_.get_id())] = i; primary_threads_.emplace_back(ptr); } for (auto* pt : primary_threads_) { status += pt->init(); } CGRAPH_FUNCTION_CHECK_STATUS /** * 策略更新: * 初始化的时候,也可以创建n个辅助线程。目的是为了配合仅使用 pool中 priority_queue 的场景 * 一般情况下,建议为0。 */ status = createSecondaryThread(config_.secondary_thread_size_); CGRAPH_FUNCTION_CHECK_STATUS is_init_ = true; CGRAPH_FUNCTION_END } CStatus UThreadPool::submit(const UTaskGroup& taskGroup, CMSec ttl) { CGRAPH_FUNCTION_BEGIN CGRAPH_ASSERT_INIT(true) std::vector> futures; for (const auto& task : taskGroup.task_arr_) { futures.emplace_back(commit(task)); } // 计算最终运行时间信息 auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(std::min(taskGroup.getTtl(), ttl)); for (auto& fut : futures) { const auto& futStatus = fut.wait_until(deadline); switch (futStatus) { case std::future_status::ready: break; // 正常情况,直接返回了 case std::future_status::timeout: status += CErrStatus("thread status timeout"); break; case std::future_status::deferred: status += CErrStatus("thread status deferred"); break; default: status += CErrStatus("thread status unknown"); } } if (taskGroup.on_finished_) { taskGroup.on_finished_(status); } CGRAPH_FUNCTION_END } CStatus UThreadPool::submit(CGRAPH_DEFAULT_CONST_FUNCTION_REF func, CMSec ttl, CGRAPH_CALLBACK_CONST_FUNCTION_REF onFinished) { return submit(UTaskGroup(func, ttl, onFinished)); } CIndex UThreadPool::getThreadIndex(CSize tid) { int threadNum = CGRAPH_SECONDARY_THREAD_COMMON_ID; auto result = thread_record_map_.find(tid); if (result != thread_record_map_.end()) { threadNum = result->second; } return threadNum; } CStatus UThreadPool::destroy() { CGRAPH_FUNCTION_BEGIN if (!is_init_) { CGRAPH_FUNCTION_END } // primary 线程是普通指针,需要delete for (auto &pt : primary_threads_) { status += pt->destroy(); } CGRAPH_FUNCTION_CHECK_STATUS /** * 这里之所以 destroy和 delete分开两个循环执行, * 是因为当前线程被delete后,还可能存在未被delete的主线程,来steal当前线程的任务 * 在windows环境下,可能出现问题。 * destroy 和 delete 分开之后,不会出现此问题。 * 感谢 Ryan大佬(https://github.com/ryanhuang) 提供的帮助 */ for (auto &pt : primary_threads_) { CGRAPH_DELETE_PTR(pt) } primary_threads_.clear(); // secondary 线程是智能指针,不需要delete task_queue_.reset(); for (auto &st : secondary_threads_) { status += st->destroy(); } CGRAPH_FUNCTION_CHECK_STATUS secondary_threads_.clear(); thread_record_map_.clear(); is_init_ = false; CGRAPH_FUNCTION_END } CBool UThreadPool::isInit() const { return is_init_; } CStatus UThreadPool::releaseSecondaryThread(CInt size) { CGRAPH_FUNCTION_BEGIN // 先将所有已经结束的,给删掉 CGRAPH_LOCK_GUARD lock(st_mutex_); for (auto iter = secondary_threads_.begin(); iter != secondary_threads_.end(); ) { !(*iter)->done_ ? secondary_threads_.erase(iter++) : iter++; } CGRAPH_RETURN_ERROR_STATUS_BY_CONDITION((size > secondary_threads_.size()), \ "cannot release [" + std::to_string(size) + "] secondary thread," \ + "only [" + std::to_string(secondary_threads_.size()) + "] left.") // 再标记几个需要删除的信息 for (auto iter = secondary_threads_.begin(); iter != secondary_threads_.end() && size-- > 0; ) { (*iter)->done_ = false; iter++; } CGRAPH_FUNCTION_END } CIndex UThreadPool::dispatch(CIndex origIndex) { CIndex realIndex = 0; if (CGRAPH_DEFAULT_TASK_STRATEGY == origIndex) { realIndex = cur_index_++; if (cur_index_ >= config_.max_thread_size_ || cur_index_ < 0) { cur_index_ = 0; } } else { realIndex = origIndex; } return realIndex; // 交到上游去判断,走哪个线程 } CStatus UThreadPool::createSecondaryThread(CInt size) { CGRAPH_FUNCTION_BEGIN int leftSize = (int)(config_.max_thread_size_ - config_.default_thread_size_ - secondary_threads_.size()); int realSize = std::min(size, leftSize); // 使用 realSize 来确保所有的线程数量之和,不会超过设定max值 CGRAPH_LOCK_GUARD lock(st_mutex_); for (int i = 0; i < realSize; i++) { auto ptr = CGRAPH_MAKE_UNIQUE_COBJECT(UThreadSecondary) ptr->setThreadPoolInfo(&task_queue_, &priority_task_queue_, &config_); status += ptr->init(); secondary_threads_.emplace_back(std::move(ptr)); } CGRAPH_FUNCTION_END } CVoid UThreadPool::monitor() { while (config_.monitor_enable_) { while (config_.monitor_enable_ && !is_init_) { // 如果没有init,则一直处于空跑状态 CGRAPH_SLEEP_SECOND(1) } auto span = config_.monitor_span_; while (config_.monitor_enable_ && is_init_ && span--) { CGRAPH_SLEEP_SECOND(1) // 保证可以快速退出 } // 如果 primary线程都在执行,则表示忙碌 bool busy = !primary_threads_.empty() && std::all_of(primary_threads_.begin(), primary_threads_.end(), [](UThreadPrimaryPtr ptr) { return nullptr != ptr && ptr->is_running_; }); CGRAPH_LOCK_GUARD lock(st_mutex_); // 如果忙碌或者priority_task_queue_中有任务,则需要添加 secondary线程 if (busy || !priority_task_queue_.empty()) { createSecondaryThread(1); } // 判断 secondary 线程是否需要退出 for (auto iter = secondary_threads_.begin(); iter != secondary_threads_.end(); ) { (*iter)->freeze() ? secondary_threads_.erase(iter++) : iter++; } } } CGRAPH_NAMESPACE_END