├── .gitignore ├── .gitlab-ci.yml ├── LICENSE ├── README.md ├── example.cpp ├── include └── smart_thread_pool.h └── manage.sh /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/c++,cmake,jetbrains,visualstudiocode 3 | 4 | ### C++ ### 5 | # Prerequisites 6 | build/ 7 | *.d 8 | 9 | # Compiled Object files 10 | *.slo 11 | *.lo 12 | *.o 13 | *.obj 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Compiled Dynamic libraries 20 | *.so 21 | *.dylib 22 | *.dll 23 | 24 | # Fortran module files 25 | *.mod 26 | *.smod 27 | 28 | # Compiled Static libraries 29 | *.lai 30 | *.la 31 | *.a 32 | *.lib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | 39 | ### CMake ### 40 | CMakeCache.txt 41 | CMakeFiles 42 | CMakeScripts 43 | Testing 44 | Makefile 45 | cmake_install.cmake 46 | install_manifest.txt 47 | compile_commands.json 48 | CTestTestfile.cmake 49 | 50 | ### JetBrains ### 51 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 52 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 53 | 54 | # User-specific stuff 55 | .idea/ 56 | .idea/**/workspace.xml 57 | .idea/**/tasks.xml 58 | .idea/**/usage.statistics.xml 59 | .idea/**/dictionaries 60 | .idea/**/shelf 61 | 62 | # Sensitive or high-churn files 63 | .idea/**/dataSources/ 64 | .idea/**/dataSources.ids 65 | .idea/**/dataSources.local.xml 66 | .idea/**/sqlDataSources.xml 67 | .idea/**/dynamic.xml 68 | .idea/**/uiDesigner.xml 69 | .idea/**/dbnavigator.xml 70 | 71 | # Gradle 72 | .idea/**/gradle.xml 73 | .idea/**/libraries 74 | 75 | # CMake 76 | cmake-build-*/ 77 | 78 | # Mongo Explorer plugin 79 | .idea/**/mongoSettings.xml 80 | 81 | # File-based project format 82 | *.iws 83 | 84 | # IntelliJ 85 | out/ 86 | 87 | # mpeltonen/sbt-idea plugin 88 | .idea_modules/ 89 | 90 | # JIRA plugin 91 | atlassian-ide-plugin.xml 92 | 93 | # Cursive Clojure plugin 94 | .idea/replstate.xml 95 | 96 | # Crashlytics plugin (for Android Studio and IntelliJ) 97 | com_crashlytics_export_strings.xml 98 | crashlytics.properties 99 | crashlytics-build.properties 100 | fabric.properties 101 | 102 | # Editor-based Rest Client 103 | .idea/httpRequests 104 | 105 | ### JetBrains Patch ### 106 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 107 | 108 | # *.iml 109 | # modules.xml 110 | # .idea/misc.xml 111 | # *.ipr 112 | 113 | # Sonarlint plugin 114 | .idea/sonarlint 115 | 116 | ### VisualStudioCode ### 117 | .vscode/* 118 | !.vscode/settings.json 119 | !.vscode/tasks.json 120 | !.vscode/launch.json 121 | !.vscode/extensions.json 122 | 123 | 124 | # End of https://www.gitignore.io/api/c++,cmake,jetbrains,visualstudiocode 125 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - cppcheck 3 | - run_example 4 | 5 | variables: 6 | DOCKER_REGISTRY: registry.cn-hangzhou.aliyuncs.com/leosocy 7 | IMAGE: ${DOCKER_REGISTRY}/stp 8 | 9 | # -------------------- Lints -------------------- 10 | cppcheck: 11 | stage: cppcheck 12 | image: 13 | name: ${DOCKER_REGISTRY}/cppcheck:1.83 14 | script: 15 | - cppcheck -j 4 --enable=warning,performance --error-exitcode=1 -I include example.cpp 16 | 17 | # -------------------- Build Example -------------------- 18 | buildexample: 19 | stage: run_example 20 | image: 21 | name: ${IMAGE}:ci 22 | script: 23 | - mkdir -p exmaple_build; cd exmaple_build 24 | - g++ ../example.cpp -I../include --std=c++11 -o example && ./example 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Leosocy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Smart Thread Pool 2 | 3 | [![pipeline status](https://gitlab.com/leosocy/SmartThreadPool/badges/master/pipeline.svg)](https://gitlab.com/leosocy/SmartThreadPool/commits/master) 4 | [![MIT licensed](https://img.shields.io/badge/license-MIT-green.svg)](https://raw.githubusercontent.com/PalmID/ppic/master/LICENSE) 5 | 6 | ## Features 7 | 8 | - Priority Task Queue 9 | - Classify Thread Pool 10 | - Self-Adaption Pool Capacity 11 | - Pools & Queues State Monitor 12 | 13 | ## Framework 14 | 15 | ![SmartThreadPoolFramework](https://user-images.githubusercontent.com/19223292/43686506-e7ae0a60-98f9-11e8-8556-e0f693db9920.jpg) 16 | 17 | ## Usage 18 | -------------------------------------------------------------------------------- /example.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************\ 2 | * Created on Mon Jul 30 2018 3 | * 4 | * The MIT License (MIT) 5 | * Copyright (c) 2018 leosocy 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the ",Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED ",AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | \*****************************************************************************/ 24 | 25 | #include "smart_thread_pool.h" 26 | #include 27 | 28 | int main(int argc, char** argv) { 29 | 30 | // ********************How to init `SmartThreadPool`******************** 31 | // 32 | // using stp::SmartThreadPool; 33 | // using stp::SmartThreadPoolBuilder; 34 | // SmartThreadPoolBuilder builder; 35 | 36 | // ********Build by calling a chain.******** 37 | // builder.AddClassifyPool(const char* pool_name, 38 | // uint8_t capacity, 39 | // uint8_t init_size); 40 | // ******** Such as: 41 | // builder.AddClassifyPool("DefaultPool", 16, 4) 42 | // .AddClassifyPool("CPUBoundPool", 8, 4) 43 | // .AddClassifyPool("IOBoundPool", 16, 8) 44 | // auto pool = builder.BuildAndInit(); // will block current thread 45 | // 46 | // *********************************************************************** 47 | 48 | // ******************************How to join a task****************************** 49 | // 50 | // pool->ApplyAsync(function, args...); 51 | // ******** Such as: 52 | // 1. Run a return careless task. 53 | // pool->ApplyAsync("IOBoundPool", TaskPriority::MEDIUM, [](){ //DoSomeThing(args...); }, arg1, arg2, ...); 54 | // 55 | // 2. Run a return careful task. 56 | // auto res = pool->ApplyAsync("CPUBoundPool", TaskPriority::HIGH, [](int count){ return count; }, 666); 57 | // auto value = res.get(); 58 | // 59 | // or you can set a timeout duration to wait for the result to become available. 60 | // 61 | // std::future_status status = res.wait_for(std::chrono::seconds(1)); // wait for 1 second. 62 | // if (status == std::future_status::ready) { 63 | // std::cout << "Result is: " << res.get() << std::endl; 64 | // } else { 65 | // std::cout << "Timeout" << std::endl; 66 | // } 67 | // 68 | // ******************************************************************************* 69 | 70 | using stp::SmartThreadPoolBuilder; 71 | using stp::TaskPriority; 72 | SmartThreadPoolBuilder builder; 73 | builder.AddClassifyPool("DefaultPool", 8, 4) 74 | .AddClassifyPool("CPUBoundPool", 8, 4) 75 | .AddClassifyPool("IOBoundPool", 64, 32) 76 | .EnableMonitor(std::chrono::seconds(5)); 77 | auto pool = builder.BuildAndInit(); 78 | 79 | for (int i = 0; i < 64; ++i) { 80 | for (unsigned char i = 0; i < 5; ++i) { 81 | pool->ApplyAsync("IOBoundPool", static_cast(i), [](unsigned char i) { 82 | std::this_thread::sleep_for(std::chrono::seconds(5)); 83 | printf("%d\n", i); 84 | }, i); 85 | } 86 | } 87 | pool->ApplyAsync("IOBoundPool", TaskPriority::HIGH, [](){ 88 | int repeat_times = 5; 89 | while(--repeat_times >= 0) { 90 | printf("IOBoundPool Task\n");std::this_thread::sleep_for(std::chrono::seconds(2)); 91 | } 92 | }); 93 | auto res = pool->ApplyAsync("CPUBoundPool", TaskPriority::MEDIUM, [](int x, int y){ 94 | return x + y; 95 | }, 1, 2); 96 | auto value = res.get(); 97 | printf("added result: %d\n", value); 98 | 99 | pool->StartAllWorkers(); 100 | } -------------------------------------------------------------------------------- /include/smart_thread_pool.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************\ 2 | * Created on Sat Jul 28 2018 3 | * 4 | * The MIT License (MIT) 5 | * Copyright (c) 2018 leosocy 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the ",Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED ",AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | \*****************************************************************************/ 24 | 25 | #ifndef SMART_THREAD_POOL_H_ 26 | #define SMART_THREAD_POOL_H_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | namespace stp { 45 | 46 | /* 47 | * 48 | * \ Workers .../ |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| 49 | * |-----ClassifyThreadPool ---->TaskPriorityQueue-->| UrgentTask HighTask MediumTask | 50 | * | |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| 51 | * | 52 | * SmartThreadPool ---->| \ Workers .../ |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| 53 | * |-----ClassifyThreadPool ---->TaskPriorityQueue-->| MediumTask LowTask DefaultTask | 54 | * | |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| 55 | * | 56 | * | \ Workers ... / |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| 57 | * |-----ClassifyThreadPool ---->TaskPriorityQueue-->| UrgentTask LowTask DefaultTask | 58 | * |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| 59 | * 60 | */ 61 | 62 | 63 | class SmartThreadPool; 64 | class ClassifyThreadPool; 65 | class Worker; 66 | 67 | class TaskPriorityQueue; 68 | class Task; 69 | 70 | class Daemon; 71 | class Monitor; 72 | 73 | enum TaskPriority : unsigned char { 74 | DEFAULT = 0, 75 | LOW, 76 | MEDIUM, 77 | HIGH, 78 | URGENT, 79 | }; 80 | 81 | namespace detail { 82 | 83 | uint8_t g_auto_increment_thread_pool_id = 0; 84 | 85 | } // namespace detail 86 | 87 | class Task { 88 | public: 89 | using TaskType = std::function; 90 | explicit Task(TaskType task, TaskPriority priority) 91 | : task_(task), priority_(priority) { 92 | } 93 | Task(const Task& other) 94 | : task_(other.task_), priority_(other.priority_) { 95 | } 96 | Task& operator=(const Task& other) { 97 | task_ = other.task_; 98 | priority_ = other.priority_; 99 | return *this; 100 | } 101 | 102 | bool operator<(const Task& rhs) const { 103 | return priority_ < rhs.priority_; 104 | } 105 | bool operator>(const Task& rhs) const { 106 | return priority_ > rhs.priority_; 107 | } 108 | 109 | TaskPriority priority() const { return priority_; } 110 | void Run() { 111 | task_(); 112 | } 113 | 114 | private: 115 | TaskType task_; 116 | TaskPriority priority_; 117 | }; 118 | 119 | class TaskPriorityQueue { 120 | public: 121 | explicit TaskPriorityQueue(const char* queue_name) 122 | : queue_name_(queue_name), alive_(true), task_count_(0), pending_task_count_(0) { 123 | } 124 | TaskPriorityQueue(TaskPriorityQueue&& other) = delete; 125 | TaskPriorityQueue(const TaskPriorityQueue&) = delete; 126 | TaskPriorityQueue& operator=(TaskPriorityQueue&& other) = delete; 127 | TaskPriorityQueue& operator=(const TaskPriorityQueue&) = delete; 128 | ~TaskPriorityQueue() { 129 | ClearQueue(); 130 | } 131 | void ClearQueue() { 132 | { 133 | std::unique_lock lock(queue_mtx_); 134 | alive_ = false; 135 | } 136 | queue_cv_.notify_all(); 137 | auto task = dequeue(); 138 | while (task) { 139 | task->Run(); 140 | task = dequeue(); 141 | } 142 | } 143 | bool empty() const { 144 | std::unique_lock lock(queue_mtx_); 145 | return tasks_.empty(); 146 | } 147 | std::size_t size() const { 148 | std::unique_lock lock(queue_mtx_); 149 | return tasks_.size(); 150 | } 151 | template 152 | auto enqueue(TaskPriority priority, F&& f, Args&&... args) -> std::future::type> { 153 | using ReturnType = typename std::result_of::type; 154 | auto task = std::make_shared< std::packaged_task >( 155 | std::bind(std::forward(f), std::forward(args)...)); 156 | { 157 | std::unique_lock lock(queue_mtx_); 158 | tasks_.emplace([task](){ (*task)(); }, priority); 159 | task_count_ += 1; 160 | pending_task_count_ += 1; 161 | } 162 | queue_cv_.notify_one(); 163 | return task->get_future(); 164 | } 165 | std::unique_ptr dequeue() { 166 | std::unique_lock lock(queue_mtx_); 167 | bool status = queue_cv_.wait_for(lock, std::chrono::seconds(5), [this]{ return !alive_ || !tasks_.empty(); }); 168 | if (!status || (!alive_ && tasks_.empty())) { 169 | return nullptr; 170 | } 171 | auto task = std::unique_ptr{new Task(std::move(tasks_.top()))}; 172 | tasks_.pop(); 173 | pending_task_count_ -= 1; 174 | return task; 175 | } 176 | const char* name() const { return queue_name_.c_str(); } 177 | uint64_t task_count() const { return task_count_; } 178 | uint64_t pending_task_count() const { return pending_task_count_; } 179 | 180 | private: 181 | std::string queue_name_; 182 | std::priority_queue tasks_; 183 | mutable std::mutex queue_mtx_; 184 | mutable std::condition_variable queue_cv_; 185 | std::atomic_bool alive_; 186 | 187 | uint64_t task_count_; 188 | uint64_t pending_task_count_; 189 | }; 190 | 191 | class Worker { 192 | public: 193 | enum State : unsigned char { 194 | IDLE = 0, 195 | BUSY, 196 | EXITED, 197 | }; 198 | explicit Worker(TaskPriorityQueue* queue) 199 | : state_(State::IDLE), completed_task_count_(0) { 200 | t_ = std::thread([queue, this]() { 201 | while (true) { 202 | auto task = queue->dequeue(); 203 | if (task) { 204 | state_ = State::BUSY; 205 | task->Run(); 206 | completed_task_count_ += 1; 207 | } else { 208 | state_ = State::EXITED; 209 | return; 210 | } 211 | } 212 | }); 213 | } 214 | void Work() { 215 | if (t_.joinable()) { 216 | t_.join(); 217 | } 218 | } 219 | State state() const { return state_; } 220 | uint64_t completed_task_count() const { return completed_task_count_; } 221 | 222 | private: 223 | std::thread t_; 224 | State state_; 225 | uint64_t completed_task_count_; 226 | }; 227 | 228 | class ClassifyThreadPool { 229 | public: 230 | ClassifyThreadPool(const char* name, uint16_t capacity) 231 | : id_(++detail::g_auto_increment_thread_pool_id), name_(name), capacity_(capacity) { 232 | workers_.reserve(capacity); 233 | ConnectTaskPriorityQueue(); 234 | } 235 | 236 | ClassifyThreadPool(ClassifyThreadPool&&) = delete; 237 | ClassifyThreadPool(const ClassifyThreadPool&) = delete; 238 | ClassifyThreadPool& operator=(ClassifyThreadPool&&) = delete; 239 | ClassifyThreadPool& operator=(const ClassifyThreadPool&) = delete; 240 | 241 | void InitWorkers(uint16_t count) { 242 | for (unsigned char i = 0; i < count && workers_.size() < capacity_; ++i) { 243 | AddWorker(); 244 | } 245 | } 246 | uint8_t id() const { return id_; } 247 | const char* name() const { return name_.c_str(); } 248 | uint16_t capacity() const { return capacity_; } 249 | uint16_t WorkerCount() const { return workers_.size(); } 250 | uint16_t IdleWorkerCount() const { 251 | return GetWorkerStateCount(Worker::State::IDLE); 252 | } 253 | uint16_t BusyWorkerCount() const { 254 | return GetWorkerStateCount(Worker::State::BUSY); 255 | } 256 | uint16_t ExitedWorkerCount() const { 257 | return GetWorkerStateCount(Worker::State::EXITED); 258 | } 259 | const std::vector& workers() const { return workers_; } 260 | const std::unique_ptr& task_queue() const { return task_queue_; } 261 | 262 | private: 263 | friend class SmartThreadPool; 264 | void ConnectTaskPriorityQueue() { 265 | std::string queue_name = name_ + "-->TaskQueue"; 266 | task_queue_ = std::unique_ptr{new TaskPriorityQueue(queue_name.c_str())}; 267 | } 268 | void AddWorker() { 269 | workers_.emplace_back(task_queue_.get()); 270 | } 271 | void StartWorkers() { 272 | for (auto& worker : workers_) { 273 | worker.Work(); 274 | } 275 | } 276 | uint16_t GetWorkerStateCount(Worker::State state) const { 277 | uint16_t count = 0; 278 | for (auto& worker : workers_) { 279 | if (worker.state() == state) { 280 | count += 1; 281 | } 282 | } 283 | return count; 284 | } 285 | uint8_t id_; 286 | std::string name_; 287 | uint16_t capacity_; 288 | std::vector workers_; 289 | std::unique_ptr task_queue_; 290 | }; 291 | 292 | class SmartThreadPool { 293 | public: 294 | SmartThreadPool(SmartThreadPool&&) = delete; 295 | SmartThreadPool(const SmartThreadPool&) = delete; 296 | SmartThreadPool& operator=(SmartThreadPool&&) = delete; 297 | SmartThreadPool& operator=(const SmartThreadPool&) = delete; 298 | 299 | template 300 | auto ApplyAsync(const char* pool_name, TaskPriority priority, 301 | F&& f, Args&&... args) -> std::future::type> { 302 | auto& pool = pools_.at(pool_name); 303 | auto res = pool->task_queue()->enqueue(priority, f, args...); 304 | if (pool->task_queue()->size() >= pool->WorkerCount() 305 | && pool->WorkerCount() < pool->capacity()) { 306 | pool->AddWorker(); 307 | } 308 | return res; 309 | } 310 | void StartAllWorkers() { 311 | for (auto&& pool : pools_) { 312 | pool.second->StartWorkers(); 313 | } 314 | } 315 | 316 | private: 317 | friend class SmartThreadPoolBuilder; 318 | friend class Monitor; 319 | SmartThreadPool() {} 320 | std::map > pools_; 321 | }; 322 | 323 | class Monitor { 324 | public: 325 | void StartMonitoring(const SmartThreadPool& pool, const std::chrono::duration& monitor_second_period) { 326 | t_ = std::move(std::thread([&pool, &monitor_second_period, this](){ 327 | while (true) { 328 | std::this_thread::sleep_for(monitor_second_period); 329 | for (auto&& pool_map : pool.pools_) { 330 | auto& classify_pool = *pool_map.second.get(); 331 | MonitorClassifyPool(classify_pool); 332 | } 333 | 334 | char now[128]; 335 | std::time_t t = std::time(NULL); 336 | std::strftime(now, sizeof(now), "%F %T", std::localtime(&t)); 337 | std::string now_str(now); 338 | 339 | std::stringstream monitor_log; 340 | auto cmp = [](const std::string& s1, const std::string& s2) { return s1.size() < s2.size(); }; 341 | size_t max_row_msg_length = 0; 342 | 343 | for (size_t i = 0; i < pool_msgs_.size(); ++i) { 344 | int max_pool_msg_length = std::max_element(pool_msgs_.begin(), pool_msgs_.end(), cmp)->length(); 345 | int max_workers_msg_length = std::max_element(workers_msgs_.begin(), workers_msgs_.end(), cmp)->length(); 346 | max_pool_msg_length += 2; 347 | max_workers_msg_length += 2; 348 | std::stringstream row_log; 349 | row_log << std::left << std::setw(max_pool_msg_length) << pool_msgs_.at(i) 350 | << std::left << std::setw(max_workers_msg_length) << workers_msgs_.at(i) 351 | << std::left << tasks_msgs_.at(i) << std::endl; 352 | if (row_log.str().length() > max_row_msg_length) { 353 | max_row_msg_length = row_log.str().length(); 354 | } 355 | monitor_log << row_log.str(); 356 | } 357 | 358 | int head_front_length = (max_row_msg_length - now_str.length()) / 2; 359 | int head_back_length = max_row_msg_length - now_str.length() - head_front_length; 360 | std::stringstream pretty_msg; 361 | pretty_msg << "/" << std::setfill('-') << std::setw(head_front_length) 362 | << "" << now << std::setfill('-') << std::setw(head_back_length - 1) 363 | << "\\" << std::endl 364 | << monitor_log.str() 365 | << "\\" << std::setfill('-') << std::setw(max_row_msg_length - 1) 366 | << "/" << std::endl; 367 | std::cout << pretty_msg.str(); 368 | pool_msgs_.clear(); 369 | workers_msgs_.clear(); 370 | tasks_msgs_.clear(); 371 | } 372 | })); 373 | 374 | t_.detach(); 375 | } 376 | 377 | private: 378 | friend class SmartThreadPoolBuilder; 379 | Monitor() {} 380 | void MonitorClassifyPool(const ClassifyThreadPool& classify_pool) { 381 | uint16_t busy_worker = classify_pool.BusyWorkerCount(); 382 | uint16_t idle_worker = classify_pool.IdleWorkerCount(); 383 | uint16_t exited_worker = classify_pool.ExitedWorkerCount(); 384 | uint16_t total_worker = classify_pool.capacity(); 385 | uint16_t assignable_worker = total_worker - classify_pool.WorkerCount(); 386 | 387 | uint64_t total_task = classify_pool.task_queue()->task_count(); 388 | uint64_t running_task = classify_pool.BusyWorkerCount(); 389 | uint64_t pending_task = classify_pool.task_queue()->pending_task_count(); 390 | uint64_t completed_task = total_task - running_task - pending_task; 391 | 392 | char pool_msg[64]; 393 | char workers_msg[128]; 394 | char tasks_msg[128]; 395 | snprintf(pool_msg, sizeof(pool_msg), " ~ ThreadPool:%s", classify_pool.name()); 396 | snprintf(workers_msg, sizeof(workers_msg), "Workers[Busy:%u, Idle:%u, Exited:%u, Assignable:%u, Total:%u]", 397 | busy_worker, idle_worker, exited_worker, assignable_worker, total_worker); 398 | snprintf(tasks_msg, sizeof(tasks_msg), "Tasks[Running:%lu, Waiting:%lu, Completed:%lu, Total:%lu]", 399 | running_task, pending_task, completed_task, total_task); 400 | 401 | pool_msgs_.emplace_back(pool_msg); 402 | workers_msgs_.emplace_back(workers_msg); 403 | tasks_msgs_.emplace_back(tasks_msg); 404 | } 405 | 406 | std::thread t_; 407 | std::vector pool_msgs_; 408 | std::vector workers_msgs_; 409 | std::vector tasks_msgs_; 410 | }; 411 | 412 | class SmartThreadPoolBuilder { 413 | public: 414 | SmartThreadPoolBuilder() 415 | : smart_pool_(new SmartThreadPool), enable_monitor_(false) { 416 | } 417 | 418 | SmartThreadPoolBuilder(SmartThreadPoolBuilder&&) = delete; 419 | SmartThreadPoolBuilder(const SmartThreadPoolBuilder&) = delete; 420 | SmartThreadPoolBuilder& operator=(SmartThreadPoolBuilder&&) = delete; 421 | SmartThreadPoolBuilder& operator=(const SmartThreadPoolBuilder&) = delete; 422 | 423 | SmartThreadPoolBuilder& AddClassifyPool(const char* pool_name, uint8_t capacity, uint8_t init_size) { 424 | auto pool = new ClassifyThreadPool(pool_name, capacity); 425 | pool->InitWorkers(init_size); 426 | smart_pool_->pools_.emplace(pool_name, pool); 427 | return *this; 428 | } 429 | SmartThreadPoolBuilder& EnableMonitor(const std::chrono::duration& second_period = std::chrono::seconds(60)) { 430 | enable_monitor_ = true; 431 | monitor_second_period_ = second_period; 432 | return *this; 433 | } 434 | std::unique_ptr BuildAndInit() { 435 | if (enable_monitor_) { 436 | auto monitor = new Monitor(); 437 | monitor->StartMonitoring(*smart_pool_.get(), monitor_second_period_); 438 | } 439 | return std::move(smart_pool_); 440 | } 441 | 442 | private: 443 | std::unique_ptr smart_pool_; 444 | bool enable_monitor_; 445 | std::chrono::duration monitor_second_period_; 446 | }; 447 | 448 | } // namespace stp 449 | 450 | #endif // SMART_THREAD_POOL_H_ 451 | -------------------------------------------------------------------------------- /manage.sh: -------------------------------------------------------------------------------- 1 | 2 | #! /bin/bash 3 | 4 | CurDir="$(cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd)" 5 | 6 | STP_CI_IMAGE=registry.cn-hangzhou.aliyuncs.com/leosocy/stp:ci 7 | STP_CONTAINER_NAME=stp 8 | BUILD_DIR=build 9 | 10 | CPP_LINT_IMAGE=registry.cn-hangzhou.aliyuncs.com/leosocy/cpplint 11 | CPP_LINT_CONTAINER_NAME=cpplint 12 | 13 | error_return() { 14 | echo "[ERROR] $1" 15 | exit 1 16 | } 17 | 18 | check_exec_success() { 19 | if [ "$1" != "0" ]; then 20 | echo "[ERROR] $2 failed!" 21 | exit 1 22 | else 23 | echo "[INFO] $2 success!" 24 | fi 25 | } 26 | 27 | updateimages() { 28 | docekr pull ${STP_CI_IMAGE} 29 | } 30 | 31 | runexample() { 32 | docker stop ${STP_CONTAINER_NAME} 2>/dev/null 33 | docker rm -v ${STP_CONTAINER_NAME} 2>/dev/null 34 | docker run -it --rm --name ${STP_CONTAINER_NAME} \ 35 | -v ${CurDir}:/home/stp -w /home/stp \ 36 | ${STP_CI_IMAGE} sh -c " \ 37 | mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} \ 38 | && g++ ../example.cpp -I../include -std=c++11 -fno-elide-constructors -o example \ 39 | && ./example 40 | " 41 | } 42 | 43 | gdbexample() { 44 | docker stop ${STP_CONTAINER_NAME} 2>/dev/null 45 | docker rm -v ${STP_CONTAINER_NAME} 2>/dev/null 46 | docker run -it --rm --name ${STP_CONTAINER_NAME} \ 47 | --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \ 48 | -v ${CurDir}:/home/stp -w /home/stp \ 49 | ${STP_CI_IMAGE} sh -c " \ 50 | mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} \ 51 | && g++ ../example.cpp -I../include -std=c++11 -fno-elide-constructors -o example \ 52 | && gdb example 53 | " 54 | } 55 | 56 | cpplint() { 57 | docker stop ${CPP_LINT_CONTAINER_NAME} 2>/dev/null 58 | docker rm -v ${CPP_LINT_CONTAINER_NAME} 2>/dev/null 59 | docker run -it --rm --name ${CPP_LINT_CONTAINER_NAME} \ 60 | -v ${CurDir}:/home/stp -w /home/stp \ 61 | ${CPP_LINT_IMAGE} sh -c " \ 62 | cpplint --linelength=120 include/smart_thread_pool.h 63 | " 64 | } 65 | 66 | ################ 67 | # script start # 68 | ################ 69 | 70 | case "$1" in 71 | runexample) runexample ;; 72 | gdbexample) gdbexample ;; 73 | cpplint) cpplint ;; 74 | updateimages) updateimages ;; 75 | *) 76 | echo "Usage:" 77 | echo "./manage.sh runexample | gdbexample" 78 | echo "./manage.sh cpplint" 79 | echo "./manage.sh updateimages" 80 | exit 1 81 | ;; 82 | esac 83 | 84 | exit 0 85 | --------------------------------------------------------------------------------