├── .gitattributes ├── .github └── workflows │ ├── cmake4mac.yml │ ├── cmake4ubuntu.yml │ └── cmake4win.yml ├── .gitignore ├── CMakeLists.txt ├── MyFunction.h ├── README.md ├── cmake └── CThreadPool-env-include.cmake ├── doc └── image │ └── CThreadPool Author.jpg ├── src ├── CBasic │ ├── CBasicDefine.h │ ├── CBasicInclude.h │ ├── CDescInfo.h │ ├── CException.h │ ├── CFuncType.h │ ├── CObject.h │ ├── CStatus.h │ ├── CStdEx.h │ ├── CStrDefine.h │ ├── CStruct.h │ └── CValType.h ├── CThreadPool.h └── UtilsCtrl │ ├── ThreadPool │ ├── Lock │ │ ├── ULockInclude.h │ │ └── USpinLock.h │ ├── Queue │ │ ├── UAtomicPriorityQueue.h │ │ ├── UAtomicQueue.h │ │ ├── UAtomicRingBufferQueue.h │ │ ├── ULockFreeRingBufferQueue.h │ │ ├── UQueueDefine.h │ │ ├── UQueueInclude.h │ │ ├── UQueueObject.h │ │ └── UWorkStealingQueue.h │ ├── Semaphore │ │ └── USemaphore.h │ ├── Task │ │ ├── UTask.h │ │ ├── UTaskGroup.h │ │ └── UTaskInclude.h │ ├── Thread │ │ ├── UThreadBase.h │ │ ├── UThreadInclude.h │ │ ├── UThreadPrimary.h │ │ └── UThreadSecondary.h │ ├── UThreadObject.h │ ├── UThreadPool.h │ ├── UThreadPool.inl │ ├── UThreadPoolConfig.h │ ├── UThreadPoolDefine.h │ └── UThreadPoolInclude.h │ ├── UAllocator.h │ ├── UtilsDefine.h │ ├── UtilsFunction.h │ ├── UtilsInclude.h │ └── UtilsObject.h └── tutorial.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # To shield the difference between Windows and Linux systems. 2 | 3 | *.h text eol=native 4 | *.cpp text eol=native 5 | *.inl text eol=native -------------------------------------------------------------------------------- /.github/workflows/cmake4mac.yml: -------------------------------------------------------------------------------- 1 | name: CMake4Mac 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: macos-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - name: Configure CMake 24 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 25 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 26 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 27 | 28 | - name: Build 29 | # Build your program with the given configuration 30 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 31 | -------------------------------------------------------------------------------- /.github/workflows/cmake4ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: CMake4Ubuntu 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - name: Configure CMake 24 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 25 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 26 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 27 | 28 | - name: Build 29 | # Build your program with the given configuration 30 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 31 | -------------------------------------------------------------------------------- /.github/workflows/cmake4win.yml: -------------------------------------------------------------------------------- 1 | name: CMake4Win 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: windows-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - name: Configure CMake 24 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 25 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 26 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 27 | 28 | - name: Build 29 | # Build your program with the given configuration 30 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /cmake-build-debug/ 2 | /cmake-build-release/ 3 | /.idea/ 4 | .DS_Store 5 | build*/ 6 | .vscode/ 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | message("* * * * * * * * * * * * * * * * *") 2 | message("* _____ _______ _____ *") 3 | message("* / ____| |__ __| | __ \\ *") 4 | message("* | | | | | |__) | *") 5 | message("* | | | | | ___/ *") 6 | message("* | |____ | | | | *") 7 | message("* \\_____| |_| |_| *") 8 | message("* * * * * * * * * * * * * * * * *") 9 | 10 | cmake_minimum_required(VERSION 3.5.0) 11 | 12 | project(CThreadPool VERSION 1.3.0) 13 | 14 | set(CMAKE_CXX_STANDARD 11) 15 | 16 | # add CThreadPool environment info 17 | include(cmake/CThreadPool-env-include.cmake) 18 | 19 | # 如果开启此宏定义,则CGraph执行过程中,不会在控制台打印任何信息 20 | # add_definitions(-D_CGRAPH_SILENCE_) 21 | 22 | # 编译libCThreadPool动态库 23 | # add_library(CThreadPool SHARED ${CTP_SRC_LIST}) 24 | 25 | # 编译libCThreadPool静态库 26 | # add_library(CThreadPool STATIC ${CTP_SRC_LIST}) 27 | 28 | add_executable(tutorial 29 | ${CTP_SRC_LIST} 30 | tutorial.cpp) 31 | -------------------------------------------------------------------------------- /MyFunction.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: MyFunction.h 5 | @Time: 2021/9/2 11:20 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_MYFUNCTION_H 10 | #define CGRAPH_MYFUNCTION_H 11 | 12 | 13 | int add(int i, int j) { 14 | return i + j; 15 | } 16 | 17 | static float minusBy5(float i) { 18 | return i - 5.0f; 19 | } 20 | 21 | 22 | class MyFunction { 23 | public: 24 | std::string concat(std::string& str) const { 25 | return info_ + str; 26 | } 27 | 28 | static int multiply(int i, int j) { 29 | return i * j; 30 | } 31 | 32 | private: 33 | std::string info_ = "MyFunction : "; 34 | }; 35 | 36 | #endif //CGRAPH_MYFUNCTION_H 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | languages 3 | os 4 | stars 5 | forks 6 |

7 | 8 |

9 | CThreadPool 说明文档 10 |

11 | 12 | ## 一. 简介 13 | `CThreadPool` 是一个跨平台的、无任何三方依赖的、head-only的、高性能的C++11(含以上版本)版本的线程池,也是 [CGraph](https://github.com/ChunelFeng/CGraph) 项目中使用的跨平台线程池组件功能的最小集。 14 | 15 | 经过CGraph和关联项目的长期迭代和验证,功能已经趋于稳定,且性能优异。因为咨询相关内容的朋友较多,故做为独立的仓库提供出来,方便大家使用。 16 | 17 | 由于是CGraph项目中的剥离出来的功能类,故在项目中保留了多处 `CGRAPH_*` 的命名方式,仅将 namespace 修改为 `CTP`,预计今后与CGraph相互独立更新。 18 | 19 | 本项目参考了[《C++并发编程实战(第二版)》](https://nj.gitbooks.io/c/content/) 中的部分内容,和github上的一些优秀实现。并在此基础上进行大量的改动、扩展和优化,在功能、性能和易用性上均有较大的提升。 20 | 21 | 在开发过程中,也沉淀了详细的说明文档(见下方 推荐阅读),以便于大家快速了解代码和思路,也请大家不吝指教。 22 | 23 | ## 二. 编译说明 24 | * 本工程支持MacOS、Linux和Windows系统,无任何第三方依赖。推荐使用C++11(默认)或以上版本,不支持以下C++11以下版本 25 | 26 | * 使用`CLion`或使用`Visual Studio 15`(或以上版本)作为IDE的开发者,打开`CMakeLists.txt`文件作为工程,即可编译通过 27 | 28 | * Linux环境开发者,在命令行模式下,输入以下指令,即可编译通过 29 | ```shell 30 | $ git clone https://github.com/ChunelFeng/CThreadPool.git 31 | $ cd CThreadPool 32 | $ cmake . -Bbuild 33 | $ cd build 34 | $ make -j8 35 | ``` 36 | 37 | ## 三. 使用Demo 38 | ```cpp 39 | #include "src/CThreadPool.h" 40 | 41 | using namespace CTP; 42 | 43 | float add_by_5(float i) { 44 | return i + 5.0f; 45 | } 46 | 47 | int main() { 48 | UThreadPool tp; 49 | int i = 6, j = 3; 50 | auto r1 = tp.commit([i, j] { return i - j; }); 51 | std::future r2 = tp.commit(std::bind(add_by_5, 8.5f)); 52 | 53 | std::cout << r1.get() << std::endl; 54 | std::cout << r2.get() << std::endl; 55 | return 0; 56 | } 57 | ``` 58 | 更多使用方法,请参考 `tutorial.cpp` 中的例子和文档中的内容。 59 | 60 | ## 四. 推荐阅读 61 | * [纯序员给你介绍图化框架的简单实现——线程池优化(一)](http://www.chunel.cn/archives/cgraph-threadpool-1-introduce) 62 | * [纯序员给你介绍图化框架的简单实现——线程池优化(二)](http://www.chunel.cn/archives/cgraph-threadpool-2-introduce) 63 | * [纯序员给你介绍图化框架的简单实现——线程池优化(三)](http://www.chunel.cn/archives/cgraph-threadpool-3-introduce) 64 | * [纯序员给你介绍图化框架的简单实现——线程池优化(四)](http://www.chunel.cn/archives/cgraph-threadpool-4-introduce) 65 | * [纯序员给你介绍图化框架的简单实现——线程池优化(五)](http://www.chunel.cn/archives/cgraph-threadpool-5-introduce) 66 | * [纯序员给你介绍图化框架的简单实现——线程池优化(六)](http://www.chunel.cn/archives/cgraph-threadpool-6-introduce) 67 | 68 | ## 五. 关联项目 69 | * [CGraph : A simple C++ DAG framework](https://github.com/ChunelFeng/CGraph) 70 | 71 | ------------ 72 | #### 附录-1. 版本信息 73 | [2022.10.05 - v1.0.0 - Chunel] 74 | * 提供线程池基本功能 75 | * 提供对应的tutorial信息 76 | 77 | [2022.10.07 - v1.0.1 - Chunel] 78 | * 提供默认开启辅助线程的配置 79 | 80 | [2022.10.11 - v1.1.0 - [MirrorYuChen](https://github.com/MirrorYuChen)] 81 | * 提供针对C++11版本的支持 82 | 83 | [2023.03.07 - v1.2.0 - Chunel] 84 | * 优化windows版本功能 85 | 86 | [2023.11.09 - v1.2.1 - Chunel] 87 | * 更新执行策略,优化整体性能 88 | * 修复辅助线程唤醒延时的问题 89 | 90 | [2025.04.17 - v1.3.0 - Chunel] 91 | * 提供 head-only 版本 92 | 93 | ------------ 94 | #### 附录-2. 联系方式 95 | * 微信: ChunelFeng 96 | * 邮箱: chunel@foxmail.com 97 | * 源码: https://github.com/ChunelFeng/CThreadPool 98 | * 论坛: www.chunel.cn 99 | 100 | ![CGraph Author](https://github.com/ChunelFeng/CThreadPool/blob/main/doc/image/CThreadPool%20Author.jpg) 101 | -------------------------------------------------------------------------------- /cmake/CThreadPool-env-include.cmake: -------------------------------------------------------------------------------- 1 | 2 | # 本cmake文件,供三方引入CGraph引用,用于屏蔽系统和C++版本的区别 3 | 4 | IF(APPLE) 5 | # 非mac平台,暂时不支持自动生成session信息 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64 -finline-functions -Wno-deprecated-declarations -Wno-c++17-extensions") 7 | add_definitions(-D_GENERATE_SESSION_) 8 | add_definitions(-D_ENABLE_LIKELY_) 9 | ELSEIF(UNIX) 10 | # linux平台,加入多线程内容 11 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -pthread -Wno-format-overflow") 12 | add_definitions(-D_ENABLE_LIKELY_) 13 | ELSEIF(WIN32) 14 | # windows平台,加入utf-8设置。否则无法通过编译 15 | add_definitions(/utf-8) 16 | 17 | # 禁止两处warning级别提示 18 | add_compile_options(/wd4996) 19 | add_compile_options(/wd4267) 20 | ENDIF() 21 | -------------------------------------------------------------------------------- /doc/image/CThreadPool Author.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChunelFeng/CThreadPool/7b2d2e572740bf0902614b073537953141fceda2/doc/image/CThreadPool Author.jpg -------------------------------------------------------------------------------- /src/CBasic/CBasicDefine.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CBasicDefine.h 5 | @Time: 2021/4/26 8:15 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_CBASICDEFINE_H 10 | #define CGRAPH_CBASICDEFINE_H 11 | 12 | #include 13 | 14 | #define CGRAPH_NAMESPACE_BEGIN \ 15 | namespace CTP { \ 16 | 17 | #define CGRAPH_NAMESPACE_END \ 18 | } /* end of namespace CTP */ \ 19 | 20 | CGRAPH_NAMESPACE_BEGIN 21 | 22 | using CCHAR = char; 23 | using CUINT = unsigned int; 24 | using CVOID = void; 25 | using CINT = int; 26 | using CLONG = long; 27 | using CULONG = unsigned long; 28 | using CBOOL = bool; 29 | using CBIGBOOL = int; 30 | using CFLOAT = float; 31 | using CDOUBLE = double; 32 | using CCONSTR = const char*; 33 | using CSIZE = size_t; 34 | 35 | CGRAPH_NAMESPACE_END 36 | 37 | #endif //CGRAPH_CBASICDEFINE_H 38 | -------------------------------------------------------------------------------- /src/CBasic/CBasicInclude.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CBasicInclude.h 5 | @Time: 2022/2/1 4:23 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_CBASICINCLUDE_H 10 | #define CGRAPH_CBASICINCLUDE_H 11 | 12 | #include "CObject.h" 13 | #include "CValType.h" 14 | #include "CFuncType.h" 15 | #include "CStatus.h" 16 | #include "CException.h" 17 | #include "CBasicDefine.h" 18 | #include "CStrDefine.h" 19 | #include "CStdEx.h" 20 | #include "CDescInfo.h" 21 | #include "CStruct.h" 22 | 23 | #endif //CGRAPH_CBASICINCLUDE_H 24 | -------------------------------------------------------------------------------- /src/CBasic/CDescInfo.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CDescInfo.h 5 | @Time: 2023/2/19 15:56 6 | @Desc: 通用描述信息 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_CDESCINFO_H 10 | #define CGRAPH_CDESCINFO_H 11 | 12 | #include 13 | 14 | #include "CBasicDefine.h" 15 | 16 | CGRAPH_NAMESPACE_BEGIN 17 | 18 | class CDescInfo { 19 | public: 20 | /** 21 | * 获取名称信息 22 | * @return 23 | */ 24 | const std::string& getName() const { 25 | return name_; 26 | } 27 | 28 | /** 29 | * 获取唯一id信息 30 | * @return 31 | */ 32 | const std::string& getSession() const { 33 | return session_; 34 | } 35 | 36 | /** 37 | * 获取描述信息 38 | * @return 39 | */ 40 | const std::string& getDescription() const { 41 | return description_; 42 | } 43 | 44 | /** 45 | * 设置名称信息 46 | * @param name 47 | * @return 48 | */ 49 | virtual auto setName(const std::string& name) 50 | -> decltype(this) { 51 | name_ = name; 52 | return this; 53 | } 54 | 55 | /** 56 | * 设置描述信息 57 | * @param description 58 | * @return 59 | */ 60 | virtual auto setDescription(const std::string& description) 61 | -> decltype(this) { 62 | description_ = description; 63 | return this; 64 | } 65 | 66 | virtual ~CDescInfo() = default; 67 | 68 | protected: 69 | std::string name_; // 名字 70 | std::string session_; // 唯一id信息 71 | std::string description_; // 描述信息 72 | }; 73 | 74 | CGRAPH_NAMESPACE_END 75 | 76 | #endif //CGRAPH_CDESCINFO_H 77 | -------------------------------------------------------------------------------- /src/CBasic/CException.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CException.h 5 | @Time: 2022/4/15 20:51 6 | @Desc: 异常处理类 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_CEXCEPTION_H 10 | #define CGRAPH_CEXCEPTION_H 11 | 12 | #include 13 | #include 14 | 15 | #include "CStrDefine.h" 16 | 17 | CGRAPH_NAMESPACE_BEGIN 18 | 19 | class CEXCEPTION : public std::exception { 20 | public: 21 | explicit CEXCEPTION(const std::string& info, 22 | const std::string& locate = CGRAPH_EMPTY) { 23 | /** 24 | * 这里的设计,和CStatus有一个联动 25 | * 如果不了解具体情况,不建议做任何修改 26 | */ 27 | exception_info_ = locate + " | " + info; 28 | } 29 | 30 | /** 31 | * 获取异常信息 32 | * @return 33 | */ 34 | const char* what() const noexcept override { 35 | return exception_info_.c_str(); 36 | } 37 | 38 | private: 39 | std::string exception_info_; // 异常状态信息 40 | }; 41 | 42 | CGRAPH_NAMESPACE_END 43 | 44 | #endif //CGRAPH_CEXCEPTION_H 45 | -------------------------------------------------------------------------------- /src/CBasic/CFuncType.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CFuncType.h 5 | @Time: 2022/2/3 1:05 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_CFUNCTYPE_H 10 | #define CGRAPH_CFUNCTYPE_H 11 | 12 | #include 13 | 14 | #include "CStrDefine.h" 15 | #include "CValType.h" 16 | 17 | CGRAPH_NAMESPACE_BEGIN 18 | 19 | using CGRAPH_DEFAULT_FUNCTION = std::function; 20 | using CGRAPH_DEFAULT_CONST_FUNCTION_REF = const std::function&; 21 | using CGRAPH_CSTATUS_FUNCTION = std::function; 22 | using CGRAPH_CSTATUS_CONST_FUNCTION_REF = const std::function&; 23 | using CGRAPH_CALLBACK_FUNCTION = std::function; 24 | using CGRAPH_CALLBACK_CONST_FUNCTION_REF = const std::function&; 25 | 26 | 27 | /** 28 | * 描述函数类型 29 | */ 30 | enum class CFunctionType { 31 | INIT = 1, /** 初始化函数 */ 32 | RUN = 2, /** 执行函数 */ 33 | DESTROY = 3 /** 释放函数 */ 34 | }; 35 | 36 | /** 开启函数流程 */ 37 | #define CGRAPH_FUNCTION_BEGIN \ 38 | CStatus status; \ 39 | 40 | /** 结束函数流程 */ 41 | #define CGRAPH_FUNCTION_END \ 42 | return status; \ 43 | 44 | /** 无任何功能函数 */ 45 | #define CGRAPH_EMPTY_FUNCTION \ 46 | return CStatus(); \ 47 | 48 | 49 | /** 获取当前代码所在的位置信息 */ 50 | #define CGRAPH_GET_LOCATE \ 51 | (std::string(__FILE__) + " | " + std::string(__FUNCTION__) \ 52 | + " | line = [" + ::std::to_string( __LINE__) + "]") 53 | 54 | 55 | /** 生成一个包含异常位置的 CStatus 56 | * 这里这样实现,是为了符合 CStatus 类似写法 57 | * */ 58 | #define CErrStatus(info) \ 59 | CStatus(info, CGRAPH_GET_LOCATE) \ 60 | 61 | /** 返回异常信息和状态 */ 62 | #define CGRAPH_RETURN_ERROR_STATUS(info) \ 63 | return CErrStatus(info); \ 64 | 65 | /** 根据条件判断是否返回错误状态 */ 66 | #define CGRAPH_RETURN_ERROR_STATUS_BY_CONDITION(cond, info) \ 67 | if (unlikely(cond)) { CGRAPH_RETURN_ERROR_STATUS(info); } \ 68 | 69 | /** 不支持当前功能 */ 70 | #define CGRAPH_NO_SUPPORT \ 71 | return CErrStatus(CGRAPH_FUNCTION_NO_SUPPORT); \ 72 | 73 | /** 定义为不能赋值和拷贝的对象类型 */ 74 | #define CGRAPH_NO_ALLOWED_COPY(CType) \ 75 | CType(const CType &) = delete; \ 76 | const CType &operator=(const CType &) = delete; \ 77 | 78 | /** 抛出异常 */ 79 | #define CGRAPH_THROW_EXCEPTION(info) \ 80 | throw CException(info, CGRAPH_GET_LOCATE); \ 81 | 82 | /** 在异常状态的情况下,抛出异常 */ 83 | #define CGRAPH_THROW_EXCEPTION_BY_STATUS(status) \ 84 | if (unlikely((status).isErr())) { \ 85 | CGRAPH_THROW_EXCEPTION((status).getInfo()); \ 86 | } \ 87 | 88 | /** 根据条件判断是否抛出异常 */ 89 | #define CGRAPH_THROW_EXCEPTION_BY_CONDITION(cond, info) \ 90 | if (unlikely(cond)) { CGRAPH_THROW_EXCEPTION(info); } \ 91 | 92 | 93 | CGRAPH_NAMESPACE_END 94 | 95 | #endif //CGRAPH_CFUNCTYPE_H 96 | -------------------------------------------------------------------------------- /src/CBasic/CObject.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CObject.h 5 | @Time: 2021/4/26 8:12 下午 6 | @Desc: 所有类型的父节点,其中run()方法必须实现 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_COBJECT_H 10 | #define CGRAPH_COBJECT_H 11 | 12 | #include "CBasicDefine.h" 13 | #include "CValType.h" 14 | #include "CFuncType.h" 15 | 16 | CGRAPH_NAMESPACE_BEGIN 17 | 18 | class CObject { 19 | public: 20 | /** 21 | * 默认构造函数 22 | */ 23 | explicit CObject() = default; 24 | 25 | /** 26 | * 初始化函数 27 | */ 28 | virtual CStatus init() { 29 | CGRAPH_EMPTY_FUNCTION 30 | } 31 | 32 | /** 33 | * 流程处理函数 34 | */ 35 | virtual CStatus run() = 0; 36 | 37 | /** 38 | * 释放函数 39 | */ 40 | virtual CStatus destroy() { 41 | CGRAPH_EMPTY_FUNCTION 42 | } 43 | 44 | /** 45 | * 默认析构函数 46 | */ 47 | virtual ~CObject() = default; 48 | }; 49 | 50 | CGRAPH_NAMESPACE_END 51 | 52 | #endif //CGRAPH_COBJECT_H 53 | -------------------------------------------------------------------------------- /src/CBasic/CStatus.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CStatus.h 5 | @Time: 2021/12/17 10:32 下午 6 | @Desc: 命名为 CSTATUS,直接对外提供的是 CStatus 类 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_CSTATUS_H 10 | #define CGRAPH_CSTATUS_H 11 | 12 | #include 13 | 14 | #include "CBasicDefine.h" 15 | #include "CStrDefine.h" 16 | 17 | CGRAPH_NAMESPACE_BEGIN 18 | 19 | /** 20 | * 说明: 21 | * 返回值为0,表示正常逻辑 22 | * 返回值为负整数,表示error逻辑,程序终止执行 23 | * 自定义返回值,请务必遵守以上约定 24 | */ 25 | static const int STATUS_OK = 0; /** 正常流程返回值 */ 26 | static const int STATUS_ERR = -1; /** 异常流程返回值 */ 27 | static const int STATUS_CRASH = -996; /** 异常流程返回值 */ 28 | static const char* STATUS_ERROR_INFO_CONNECTOR = " && "; /** 多异常信息连接符号 */ 29 | 30 | class CSTATUS { 31 | public: 32 | explicit CSTATUS() = default; 33 | 34 | explicit CSTATUS(const std::string &errorInfo, 35 | const std::string &locateInfo = CGRAPH_EMPTY) { 36 | this->error_code_ = STATUS_ERR; // 默认的error code信息 37 | this->error_info_ = errorInfo; 38 | this->error_locate_ = locateInfo; 39 | } 40 | 41 | explicit CSTATUS(int errorCode, const std::string &errorInfo, 42 | const std::string &locateInfo = CGRAPH_EMPTY) { 43 | this->error_code_ = errorCode; 44 | this->error_info_ = errorInfo; 45 | this->error_locate_ = locateInfo; 46 | } 47 | 48 | CSTATUS(const CSTATUS &status) { 49 | if (status.isOK()) { 50 | return; 51 | } 52 | 53 | this->error_code_ = status.error_code_; 54 | this->error_info_ = status.error_info_; 55 | this->error_locate_ = status.error_locate_; 56 | } 57 | 58 | CSTATUS(const CSTATUS &&status) noexcept { 59 | if (status.isOK()) { 60 | return; 61 | } 62 | 63 | this->error_code_ = status.error_code_; 64 | this->error_info_ = status.error_info_; 65 | this->error_locate_ = status.error_locate_; 66 | } 67 | 68 | CSTATUS& operator=(const CSTATUS& status) { 69 | if (!status.isOK()) { 70 | // 如果status是正常的话,则所有数据保持不变 71 | this->error_code_ = status.error_code_; 72 | this->error_info_ = status.error_info_; 73 | this->error_locate_ = status.error_locate_; 74 | } 75 | return (*this); 76 | } 77 | 78 | CSTATUS& operator+=(const CSTATUS& cur) { 79 | /** 80 | * 如果当前状态已经异常,则不做改动 81 | * 如果当前状态正常,并且传入的状态是异常的话,则返回异常 82 | */ 83 | if (!this->isErr() && cur.isErr()) { 84 | this->error_code_ = cur.error_code_; 85 | this->error_info_ = cur.error_info_; 86 | this->error_locate_ = cur.error_locate_; 87 | } 88 | 89 | return (*this); 90 | } 91 | 92 | /** 93 | * 获取异常值信息 94 | * @return 95 | */ 96 | int getCode() const { 97 | return this->error_code_; 98 | } 99 | 100 | /** 101 | * 获取异常信息 102 | * @return 103 | */ 104 | const std::string& getInfo() const { 105 | return this->error_info_; 106 | } 107 | 108 | /** 109 | * 获取报错位置 110 | * @return 111 | */ 112 | const std::string& getLocate() const { 113 | return this->error_locate_; 114 | } 115 | 116 | /** 117 | * 判断当前状态是否可行 118 | * @return 119 | */ 120 | bool isOK() const { 121 | return STATUS_OK == error_code_; 122 | } 123 | 124 | /** 125 | * 判断当前状态是否可行 126 | * @return 127 | */ 128 | bool isErr() const { 129 | return error_code_ < STATUS_OK; // 约定异常信息,均为负值 130 | } 131 | 132 | /** 133 | * 判断当前状态是否是崩溃了 134 | * @return 135 | */ 136 | bool isCrash() const { 137 | return STATUS_CRASH == error_code_; 138 | } 139 | 140 | private: 141 | int error_code_ = STATUS_OK; // 错误码信息 142 | std::string error_info_; // 错误信息描述 143 | std::string error_locate_; // 错误发生的具体位置,形如:file|function|line 144 | }; 145 | 146 | CGRAPH_NAMESPACE_END 147 | 148 | #endif //CGRAPH_CSTATUS_H 149 | -------------------------------------------------------------------------------- /src/CBasic/CStdEx.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CStdEx.h 5 | @Time: 2023/1/31 23:15 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_CSTDEX_H 10 | #define CGRAPH_CSTDEX_H 11 | 12 | #include 13 | #include 14 | 15 | CGRAPH_NAMESPACE_BEGIN 16 | 17 | // 兼容 std::enable_if_t 的语法 18 | template 19 | using c_enable_if_t = typename std::enable_if::type; 20 | 21 | // 兼容 std::make_unique 的语法 22 | template 23 | typename std::unique_ptr c_make_unique(Args&&... args) { 24 | return std::unique_ptr(new T(std::forward(args)...)); 25 | } 26 | 27 | CGRAPH_NAMESPACE_END 28 | 29 | #endif //CGRAPH_CSTDEX_H 30 | -------------------------------------------------------------------------------- /src/CBasic/CStrDefine.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CInfoDefine.h 5 | @Time: 2022/4/16 14:01 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_CSTRDEFINE_H 10 | #define CGRAPH_CSTRDEFINE_H 11 | 12 | #include 13 | 14 | #include "CBasicDefine.h" 15 | 16 | CGRAPH_NAMESPACE_BEGIN 17 | 18 | static const std::string& CGRAPH_EMPTY = ""; 19 | static const std::string& CGRAPH_DEFAULT = "default"; 20 | static const std::string& CGRAPH_UNKNOWN = "unknown"; 21 | static const std::string& CGRAPH_BASIC_EXCEPTION = "CGraph default exception"; 22 | static const std::string& CGRAPH_FUNCTION_NO_SUPPORT = "CGraph function no support"; 23 | static const std::string& CGRAPH_INPUT_IS_NULL = "input is nullptr"; 24 | 25 | CGRAPH_NAMESPACE_END 26 | 27 | #endif //CGRAPH_CSTRDEFINE_H 28 | -------------------------------------------------------------------------------- /src/CBasic/CStruct.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CStruct.h 5 | @Time: 2023/7/16 11:36 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_CSTRUCT_H 10 | #define CGRAPH_CSTRUCT_H 11 | 12 | #include "CBasicDefine.h" 13 | 14 | CGRAPH_NAMESPACE_BEGIN 15 | 16 | /** 17 | * 所有框架内部结构体定义的基类 18 | * 仅针对类似 bean 数据类型的定义 19 | */ 20 | class CStruct { 21 | }; 22 | 23 | CGRAPH_NAMESPACE_END 24 | 25 | #endif //CGRAPH_CSTRUCT_H 26 | -------------------------------------------------------------------------------- /src/CBasic/CValType.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CValTypes.h 5 | @Time: 2022/2/3 12:58 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_CVALTYPE_H 10 | #define CGRAPH_CVALTYPE_H 11 | 12 | #include "CBasicDefine.h" 13 | #include "CStatus.h" 14 | #include "CException.h" 15 | 16 | using CChar = CTP::CCHAR; 17 | using CUint = CTP::CUINT; 18 | using CSize = CTP::CSIZE; 19 | using CVoid = CTP::CVOID; 20 | using CVoidPtr = CTP::CVOID *; 21 | using CInt = CTP::CINT; 22 | using CLong = CTP::CLONG; 23 | using CULong = CTP::CULONG; 24 | using CBool = CTP::CBOOL; 25 | using CIndex = CTP::CINT; // 表示标识信息,可以为负数 26 | using CFloat = CTP::CFLOAT; 27 | using CDouble = CTP::CDOUBLE; 28 | using CConStr = CTP::CCONSTR; // 表示 const char* 29 | using CBigBool = CTP::CBIGBOOL; 30 | 31 | using CLevel = CTP::CINT; 32 | using CSec = CTP::CLONG; // 表示秒信息, for second 33 | using CMSec = CTP::CLONG; // 表示毫秒信息, for millisecond 34 | using CFMSec = CTP::CDOUBLE; // 表示毫秒信息,包含小数点信息 35 | 36 | using CStatus = CTP::CSTATUS; 37 | using CException = CTP::CEXCEPTION; 38 | 39 | #endif //CGRAPH_CVALTYPE_H 40 | -------------------------------------------------------------------------------- /src/CThreadPool.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CThreadPool.h 5 | @Time: 2022/10/5 20:08 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CTHREADPOOL_CTHREADPOOL_H 10 | #define CTHREADPOOL_CTHREADPOOL_H 11 | 12 | #include "CBasic/CBasicInclude.h" 13 | #include "UtilsCtrl/UtilsInclude.h" 14 | 15 | #endif //CTHREADPOOL_CTHREADPOOL_H 16 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Lock/ULockInclude.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: ULockInclude.h 5 | @Time: 2023/2/21 22:19 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_ULOCKINCLUDE_H 10 | #define CGRAPH_ULOCKINCLUDE_H 11 | 12 | #include "USpinLock.h" 13 | 14 | #endif //CGRAPH_ULOCKINCLUDE_H 15 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Lock/USpinLock.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: USpinLock.h 5 | @Time: 2023/2/21 22:17 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_USPINLOCK_H 10 | #define CGRAPH_USPINLOCK_H 11 | 12 | #include 13 | 14 | #include "../UThreadObject.h" 15 | 16 | CGRAPH_NAMESPACE_BEGIN 17 | 18 | class USpinLock : public UThreadObject { 19 | public: 20 | /** 21 | * 加锁 22 | */ 23 | CVoid lock() { 24 | // memory_order_acquire 后面访存指令勿重排至此条指令之前 25 | while (flag_.test_and_set(std::memory_order_acquire)) { 26 | } 27 | } 28 | 29 | /** 30 | * 解锁 31 | */ 32 | CVoid unlock() { 33 | // memory_order_release 前面访存指令勿重排到此条指令之后 34 | flag_.clear(std::memory_order_release); 35 | } 36 | 37 | /** 38 | * 尝试加锁。若未加锁,会上锁 39 | * @return 40 | */ 41 | CBool tryLock() { 42 | return !flag_.test_and_set(); 43 | } 44 | 45 | private: 46 | std::atomic_flag flag_ = ATOMIC_FLAG_INIT; // 标志位 47 | }; 48 | 49 | CGRAPH_NAMESPACE_END 50 | 51 | #endif //CGRAPH_USPINLOCK_H 52 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Queue/UAtomicPriorityQueue.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UAtomicPriorityQueue.h 5 | @Time: 2022/10/1 21:40 6 | @Desc: 线程安全的优先队列。因为 priority_queue和queue的弹出方式不一致,故暂时不做合并 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UATOMICPRIORITYQUEUE_H 10 | #define CGRAPH_UATOMICPRIORITYQUEUE_H 11 | 12 | #include 13 | 14 | #include "UQueueObject.h" 15 | 16 | CGRAPH_NAMESPACE_BEGIN 17 | 18 | template 19 | class UAtomicPriorityQueue : public UQueueObject { 20 | public: 21 | UAtomicPriorityQueue() = default; 22 | 23 | /** 24 | * 尝试弹出 25 | * @param value 26 | * @return 27 | */ 28 | CBool tryPop(T& value) { 29 | CBool result = false; 30 | if (mutex_.try_lock()) { 31 | if (!priority_queue_.empty()) { 32 | value = std::move(*priority_queue_.top()); 33 | priority_queue_.pop(); 34 | result = true; 35 | } 36 | mutex_.unlock(); 37 | } 38 | 39 | return result; 40 | } 41 | 42 | 43 | /** 44 | * 尝试弹出多个任务 45 | * @param values 46 | * @param maxPoolBatchSize 47 | * @return 48 | */ 49 | CBool tryPop(std::vector& values, int maxPoolBatchSize) { 50 | CBool result = false; 51 | if (mutex_.try_lock()) { 52 | while (!priority_queue_.empty() && maxPoolBatchSize-- > 0) { 53 | values.emplace_back(std::move(*priority_queue_.top())); 54 | priority_queue_.pop(); 55 | result = true; 56 | } 57 | mutex_.unlock(); 58 | } 59 | 60 | return result; 61 | } 62 | 63 | 64 | /** 65 | * 传入数据 66 | * @param value 67 | * @param priority 任务优先级,数字排序 68 | * @return 69 | */ 70 | CVoid push(T&& value, int priority) { 71 | std::unique_ptr task(c_make_unique(std::move(value), priority)); 72 | CGRAPH_LOCK_GUARD lk(mutex_); 73 | priority_queue_.push(std::move(task)); 74 | } 75 | 76 | 77 | /** 78 | * 判定队列是否为空 79 | * @return 80 | */ 81 | CBool empty() { 82 | CGRAPH_LOCK_GUARD lk(mutex_); 83 | return priority_queue_.empty(); 84 | } 85 | 86 | CGRAPH_NO_ALLOWED_COPY(UAtomicPriorityQueue) 87 | 88 | private: 89 | std::priority_queue > priority_queue_; // 优先队列信息,根据重要级别决定先后执行顺序 90 | }; 91 | 92 | CGRAPH_NAMESPACE_END 93 | 94 | #endif //CGRAPH_UATOMICPRIORITYQUEUE_H 95 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Queue/UAtomicQueue.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UAtomicQueue.h 5 | @Time: 2021/7/2 11:28 下午 6 | @Desc: 设计了一个安全队列 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UATOMICQUEUE_H 10 | #define CGRAPH_UATOMICQUEUE_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "../UThreadPoolDefine.h" 18 | #include "UQueueObject.h" 19 | 20 | CGRAPH_NAMESPACE_BEGIN 21 | 22 | template 23 | class UAtomicQueue : public UQueueObject { 24 | public: 25 | UAtomicQueue() = default; 26 | 27 | /** 28 | * 等待弹出 29 | * @param value 30 | */ 31 | CVoid waitPop(T& value) { 32 | CGRAPH_UNIQUE_LOCK lk(mutex_); 33 | cv_.wait(lk, [this] { return !queue_.empty(); }); 34 | value = std::move(*queue_.front()); 35 | queue_.pop(); 36 | } 37 | 38 | 39 | /** 40 | * 尝试弹出 41 | * @param value 42 | * @return 43 | */ 44 | CBool tryPop(T& value) { 45 | CBool result = false; 46 | if (!queue_.empty() && mutex_.try_lock()) { 47 | if (!queue_.empty()) { 48 | value = std::move(*queue_.front()); 49 | queue_.pop(); 50 | result = true; 51 | } 52 | mutex_.unlock(); 53 | } 54 | 55 | return result; 56 | } 57 | 58 | 59 | /** 60 | * 尝试弹出多个任务 61 | * @param values 62 | * @param maxPoolBatchSize 63 | * @return 64 | */ 65 | CBool tryPop(std::vector& values, int maxPoolBatchSize) { 66 | CBool result = false; 67 | if (!queue_.empty() && mutex_.try_lock()) { 68 | while (!queue_.empty() && maxPoolBatchSize-- > 0) { 69 | values.emplace_back(std::move(*queue_.front())); 70 | queue_.pop(); 71 | result = true; 72 | } 73 | mutex_.unlock(); 74 | } 75 | 76 | return result; 77 | } 78 | 79 | 80 | /** 81 | * 阻塞式等待弹出 82 | * @return 83 | */ 84 | std::unique_ptr popWithTimeout(CMSec ms) { 85 | CGRAPH_UNIQUE_LOCK lk(mutex_); 86 | if (!cv_.wait_for(lk, std::chrono::milliseconds(ms), 87 | [this] { return (!queue_.empty()) || (!ready_flag_); })) { 88 | return nullptr; 89 | } 90 | if (queue_.empty() || !ready_flag_) { 91 | return nullptr; 92 | } 93 | 94 | std::unique_ptr result = std::move(queue_.front()); 95 | queue_.pop(); // 如果等成功了,则弹出一个信息 96 | return result; 97 | } 98 | 99 | 100 | /** 101 | * 非阻塞式等待弹出 102 | * @return 103 | */ 104 | std::unique_ptr tryPop() { 105 | CGRAPH_LOCK_GUARD lk(mutex_); 106 | if (queue_.empty()) { return std::unique_ptr(); } 107 | std::unique_ptr ptr = std::move(queue_.front()); 108 | queue_.pop(); 109 | return ptr; 110 | } 111 | 112 | 113 | /** 114 | * 传入数据 115 | * @param value 116 | */ 117 | CVoid push(T&& value) { 118 | std::unique_ptr::type> \ 119 | task(c_make_unique::type>(std::forward(value))); 120 | while (true) { 121 | if (mutex_.try_lock()) { 122 | queue_.push(std::move(task)); 123 | mutex_.unlock(); 124 | break; 125 | } else { 126 | std::this_thread::yield(); 127 | } 128 | } 129 | cv_.notify_one(); 130 | } 131 | 132 | 133 | /** 134 | * 判定队列是否为空 135 | * @return 136 | */ 137 | CBool empty() { 138 | CGRAPH_LOCK_GUARD lk(mutex_); 139 | return queue_.empty(); 140 | } 141 | 142 | /** 143 | * 功能是通知所有的辅助线程停止工作 144 | * @return 145 | */ 146 | CVoid reset() { 147 | ready_flag_ = false; 148 | cv_.notify_all(); 149 | } 150 | 151 | /** 152 | * 初始化状态 153 | * @return 154 | */ 155 | CVoid setup() { 156 | ready_flag_ = true; 157 | queue_ = {}; 158 | } 159 | 160 | CGRAPH_NO_ALLOWED_COPY(UAtomicQueue) 161 | 162 | private: 163 | std::queue> queue_; // 任务队列 164 | CBool ready_flag_ { true }; // 执行标记,主要用于快速释放 destroy 逻辑中,多个辅助线程等待的状态 165 | }; 166 | 167 | CGRAPH_NAMESPACE_END 168 | 169 | #endif //CGRAPH_UATOMICQUEUE_H 170 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Queue/UAtomicRingBufferQueue.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UAtomicRingBufferQueue.h 5 | @Time: 2022/10/22 22:32 6 | @Desc: 本 queue 仅支持单入单出模式 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UATOMICRINGBUFFERQUEUE_H 10 | #define CGRAPH_UATOMICRINGBUFFERQUEUE_H 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "UQueueObject.h" 17 | 18 | CGRAPH_NAMESPACE_BEGIN 19 | 20 | template 21 | class UAtomicRingBufferQueue : public UQueueObject { 22 | public: 23 | explicit UAtomicRingBufferQueue() { 24 | head_ = 0; 25 | tail_ = 0; 26 | capacity_ = capacity; 27 | ring_buffer_queue_.resize(capacity_); 28 | } 29 | 30 | ~UAtomicRingBufferQueue() override { 31 | clear(); 32 | } 33 | 34 | /** 35 | * 设置容量信息 36 | * @param size 37 | * @return 38 | * @notice 谨慎使用,push信息之后,不推荐使用 39 | */ 40 | UAtomicRingBufferQueue* setCapacity(CUint size) { 41 | capacity_ = size; 42 | ring_buffer_queue_.resize(capacity_); 43 | return this; 44 | } 45 | 46 | /** 47 | * 获取容量信息 48 | * @return 49 | */ 50 | CUint getCapacity() const { 51 | return capacity_; 52 | } 53 | 54 | /** 55 | * 写入信息 56 | * @tparam TImpl 57 | * @param value 58 | * @param strategy 59 | * @return 60 | */ 61 | template 62 | CVoid push(const TImpl& value, URingBufferPushStrategy strategy) { 63 | { 64 | CGRAPH_UNIQUE_LOCK lk(mutex_); 65 | if (isFull()) { 66 | switch (strategy) { 67 | case URingBufferPushStrategy::WAIT: 68 | push_cv_.wait(lk, [this] { return !isFull(); }); 69 | break; 70 | case URingBufferPushStrategy::REPLACE: 71 | head_ = (head_ + 1) % capacity_; 72 | break; 73 | case URingBufferPushStrategy::DROP: 74 | return; // 直接返回,不写入即可 75 | } 76 | } 77 | 78 | ring_buffer_queue_[tail_] = std::move(c_make_unique(value)); 79 | tail_ = (tail_ + 1) % capacity_; 80 | } 81 | pop_cv_.notify_one(); 82 | } 83 | 84 | /** 85 | * 写入智能指针类型的信息 86 | * @tparam TImpl 87 | * @param value 88 | * @param strategy 89 | * @return 90 | */ 91 | template 92 | CVoid push(std::unique_ptr& value, URingBufferPushStrategy strategy) { 93 | { 94 | CGRAPH_UNIQUE_LOCK lk(mutex_); 95 | if (isFull()) { 96 | switch (strategy) { 97 | case URingBufferPushStrategy::WAIT: 98 | push_cv_.wait(lk, [this] { return !isFull(); }); 99 | break; 100 | case URingBufferPushStrategy::REPLACE: 101 | head_ = (head_ + 1) % capacity_; 102 | break; 103 | case URingBufferPushStrategy::DROP: 104 | return; // 直接返回,不写入即可 105 | } 106 | } 107 | 108 | ring_buffer_queue_[tail_] = std::move(value); 109 | tail_ = (tail_ + 1) % capacity_; 110 | } 111 | pop_cv_.notify_one(); 112 | } 113 | 114 | /** 115 | * 等待弹出信息 116 | * @param value 117 | * @param timeout 118 | * @return 119 | */ 120 | template 121 | CStatus waitPopWithTimeout(TImpl& value, CMSec timeout) { 122 | CGRAPH_FUNCTION_BEGIN 123 | { 124 | CGRAPH_UNIQUE_LOCK lk(mutex_); 125 | if (isEmpty() 126 | && !pop_cv_.wait_for(lk, std::chrono::milliseconds(timeout), 127 | [this] { return !isEmpty(); })) { 128 | // 如果timeout的时间内,等不到消息,则返回错误信息 129 | CGRAPH_RETURN_ERROR_STATUS("receive message timeout.") 130 | } 131 | 132 | value = *ring_buffer_queue_[head_]; // 这里直接进行值copy 133 | head_ = (head_ + 1) % capacity_; 134 | } 135 | push_cv_.notify_one(); 136 | CGRAPH_FUNCTION_END 137 | } 138 | 139 | /** 140 | * 等待弹出信息。ps:当入参为智能指针的情况 141 | * @tparam TImpl 142 | * @param value 143 | * @param timeout 144 | * @return 145 | */ 146 | template 147 | CStatus waitPopWithTimeout(std::unique_ptr& value, CMSec timeout) { 148 | CGRAPH_FUNCTION_BEGIN 149 | { 150 | CGRAPH_UNIQUE_LOCK lk(mutex_); 151 | if (isEmpty() 152 | && !pop_cv_.wait_for(lk, std::chrono::milliseconds(timeout), 153 | [this] { return !isEmpty(); })) { 154 | // 如果timeout的时间内,等不到消息,则返回错误信息 155 | CGRAPH_RETURN_ERROR_STATUS("receive message timeout.") 156 | } 157 | 158 | /** 159 | * 当传入的内容,是智能指针的时候, 160 | * 这里就直接通过 move转移过去好了,跟直接传值的方式,保持区别 161 | */ 162 | value = std::move(ring_buffer_queue_[head_]); 163 | head_ = (head_ + 1) % capacity_; 164 | } 165 | push_cv_.notify_one(); 166 | CGRAPH_FUNCTION_END 167 | } 168 | 169 | /** 170 | * 清空所有的数据 171 | * @return 172 | */ 173 | CStatus clear() { 174 | CGRAPH_FUNCTION_BEGIN 175 | ring_buffer_queue_.resize(0); 176 | head_ = 0; 177 | tail_ = 0; 178 | CGRAPH_FUNCTION_END 179 | } 180 | 181 | protected: 182 | /** 183 | * 当前队列是否为满 184 | * @return 185 | */ 186 | CBool isFull() { 187 | // 空出来一个位置,这个时候不让 tail写入 188 | return head_ == (tail_ + 1) % capacity_; 189 | } 190 | 191 | /** 192 | * 当前队列是否为空 193 | * @return 194 | */ 195 | CBool isEmpty() { 196 | return head_ == tail_; 197 | } 198 | 199 | CGRAPH_NO_ALLOWED_COPY(UAtomicRingBufferQueue) 200 | 201 | private: 202 | CUint head_; // 头结点位置 203 | CUint tail_; // 尾结点位置 204 | CUint capacity_; // 环形缓冲的容量大小 205 | 206 | std::condition_variable push_cv_; // 写入的条件变量。为了保持语义完整,也考虑今后多入多出的可能性,不使用 父类中的 cv_了 207 | std::condition_variable pop_cv_; // 读取的条件变量 208 | 209 | std::vector > ring_buffer_queue_; // 环形缓冲区 210 | }; 211 | 212 | CGRAPH_NAMESPACE_END 213 | 214 | #endif //CGRAPH_UATOMICRINGBUFFERQUEUE_H 215 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Queue/ULockFreeRingBufferQueue.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: ULockFreeRingBufferQueue.h 5 | @Time: 2023/10/7 21:35 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_ULOCKFREERINGBUFFERQUEUE_H 10 | #define CGRAPH_ULOCKFREERINGBUFFERQUEUE_H 11 | 12 | #include 13 | #include 14 | 15 | #include "UQueueObject.h" 16 | 17 | CGRAPH_NAMESPACE_BEGIN 18 | 19 | template 20 | class ULockFreeRingBufferQueue : public UQueueObject { 21 | public: 22 | explicit ULockFreeRingBufferQueue() { 23 | head_ = 0; 24 | tail_ = 0; 25 | ring_buffer_.resize(CAPACITY); 26 | } 27 | 28 | ~ULockFreeRingBufferQueue() override { 29 | ring_buffer_.clear(); 30 | } 31 | 32 | /** 33 | * 写入一个任务 34 | * @param value 35 | */ 36 | CVoid push(T&& value) { 37 | int curTail = tail_.load(std::memory_order_relaxed); 38 | int nextTail = (curTail + 1) % CAPACITY; 39 | 40 | while (nextTail == head_.load(std::memory_order_acquire)) { 41 | // 队列已满,等待其他线程出队 42 | std::this_thread::yield(); 43 | } 44 | 45 | ring_buffer_[curTail] = std::move(value); 46 | tail_.store(nextTail, std::memory_order_release); 47 | } 48 | 49 | /** 50 | * 尝试弹出一个任务 51 | * @param value 52 | * @return 53 | */ 54 | CBool tryPop(T& value) { 55 | int curHead = head_.load(std::memory_order_relaxed); 56 | if (curHead == tail_.load(std::memory_order_acquire)) { 57 | // 队列已空,直接返回false 58 | return false; 59 | } 60 | 61 | value = std::move(ring_buffer_[curHead]); 62 | 63 | int nextHead = (curHead + 1) % CAPACITY; 64 | head_.store(nextHead, std::memory_order_release); 65 | return true; 66 | } 67 | 68 | 69 | private: 70 | std::atomic head_; // 开始元素(较早写入的)的位置 71 | std::atomic tail_; // 尾部的位置 72 | std::vector > ring_buffer_; // 环形队列 73 | }; 74 | 75 | CGRAPH_NAMESPACE_END 76 | 77 | #endif //CGRAPH_ULOCKFREERINGBUFFERQUEUE_H 78 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Queue/UQueueDefine.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UQueueDefine.h 5 | @Time: 2023/9/15 21:31 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UQUEUEDEFINE_H 10 | #define CGRAPH_UQUEUEDEFINE_H 11 | 12 | CGRAPH_NAMESPACE_BEGIN 13 | 14 | /** 当环形队列满的时候,写入信息时候的策略 */ 15 | enum class URingBufferPushStrategy { 16 | WAIT = 1, // 等待有数据被消费后,再写入 17 | REPLACE = 2, // 替换未被消费的最早进入的内容 18 | DROP = 3, // 丢弃当前信息 19 | }; 20 | 21 | CGRAPH_NAMESPACE_END 22 | 23 | #endif //CGRAPH_UQUEUEDEFINE_H 24 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Queue/UQueueInclude.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UQueueInclude.h 5 | @Time: 2022/1/12 11:09 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UQUEUEINCLUDE_H 10 | #define CGRAPH_UQUEUEINCLUDE_H 11 | 12 | #include "UAtomicQueue.h" 13 | #include "UWorkStealingQueue.h" 14 | #include "UAtomicPriorityQueue.h" 15 | #include "UAtomicRingBufferQueue.h" 16 | #include "ULockFreeRingBufferQueue.h" 17 | 18 | #endif //CGRAPH_UQUEUEINCLUDE_H 19 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Queue/UQueueObject.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UQueueObject.h 5 | @Time: 2022/10/1 20:31 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UQUEUEOBJECT_H 10 | #define CGRAPH_UQUEUEOBJECT_H 11 | 12 | #include 13 | 14 | #include "../UThreadObject.h" 15 | #include "UQueueDefine.h" 16 | 17 | CGRAPH_NAMESPACE_BEGIN 18 | 19 | class UQueueObject : public UThreadObject { 20 | protected: 21 | std::mutex mutex_; 22 | std::condition_variable cv_; 23 | }; 24 | 25 | CGRAPH_NAMESPACE_END 26 | 27 | #endif //CGRAPH_UQUEUEOBJECT_H 28 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Queue/UWorkStealingQueue.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UWorkStealingQueue.h 5 | @Time: 2021/7/2 11:29 下午 6 | @Desc: 实现了一个包含盗取功能的安全队列 7 | ***************************/ 8 | 9 | 10 | #ifndef CGRAPH_UWORKSTEALINGQUEUE_H 11 | #define CGRAPH_UWORKSTEALINGQUEUE_H 12 | 13 | #include 14 | 15 | #include "UQueueObject.h" 16 | 17 | CGRAPH_NAMESPACE_BEGIN 18 | 19 | template 20 | class UWorkStealingQueue : public UQueueObject { 21 | public: 22 | /** 23 | * 向队列中写入信息 24 | * @param task 25 | */ 26 | CVoid push(T&& task) { 27 | while (true) { 28 | if (lock_.try_lock()) { 29 | deque_.emplace_front(std::forward(task)); 30 | lock_.unlock(); 31 | break; 32 | } else { 33 | std::this_thread::yield(); 34 | } 35 | } 36 | } 37 | 38 | 39 | /** 40 | * 尝试往队列里写入信息 41 | * @param task 42 | * @return 43 | */ 44 | CBool tryPush(T&& task) { 45 | CBool result = false; 46 | if (lock_.try_lock()) { 47 | deque_.emplace_back(std::forward(task)); 48 | lock_.unlock(); 49 | result = true; 50 | } 51 | return result; 52 | } 53 | 54 | 55 | /** 56 | * 向队列中写入信息 57 | * @param task 58 | */ 59 | CVoid push(std::vector& tasks) { 60 | while (true) { 61 | if (lock_.try_lock()) { 62 | for (const auto& task : tasks) { 63 | deque_.emplace_front(std::forward(task)); 64 | } 65 | lock_.unlock(); 66 | break; 67 | } else { 68 | std::this_thread::yield(); 69 | } 70 | } 71 | } 72 | 73 | 74 | /** 75 | * 尝试批量写入内容 76 | * @param tasks 77 | * @return 78 | */ 79 | CBool tryPush(std::vector& tasks) { 80 | CBool result = false; 81 | if (lock_.try_lock()) { 82 | for (const auto& task : tasks) { 83 | deque_.emplace_back(std::forward(task)); 84 | } 85 | lock_.unlock(); 86 | result = true; 87 | } 88 | return result; 89 | } 90 | 91 | 92 | /** 93 | * 弹出节点,从头部进行 94 | * @param task 95 | * @return 96 | */ 97 | CBool tryPop(T& task) { 98 | // 这里不使用raii锁,主要是考虑到多线程的情况下,可能会重复进入 99 | bool result = false; 100 | if (!deque_.empty() && lock_.try_lock()) { 101 | if (!deque_.empty()) { 102 | task = std::forward(deque_.front()); // 从前方弹出 103 | deque_.pop_front(); 104 | result = true; 105 | } 106 | lock_.unlock(); 107 | } 108 | 109 | return result; 110 | } 111 | 112 | 113 | /** 114 | * 从头部开始批量获取可执行任务信息 115 | * @param taskArr 116 | * @param maxLocalBatchSize 117 | * @return 118 | */ 119 | CBool tryPop(std::vector& taskArr, int maxLocalBatchSize) { 120 | bool result = false; 121 | if (!deque_.empty() && lock_.try_lock()) { 122 | while (!deque_.empty() && maxLocalBatchSize--) { 123 | taskArr.emplace_back(std::forward(deque_.front())); 124 | deque_.pop_front(); 125 | result = true; 126 | } 127 | lock_.unlock(); 128 | } 129 | 130 | return result; 131 | } 132 | 133 | 134 | /** 135 | * 窃取节点,从尾部进行 136 | * @param task 137 | * @return 138 | */ 139 | CBool trySteal(T& task) { 140 | bool result = false; 141 | if (!deque_.empty() && lock_.try_lock()) { 142 | if (!deque_.empty()) { 143 | task = std::forward(deque_.back()); // 从后方窃取 144 | deque_.pop_back(); 145 | result = true; 146 | } 147 | lock_.unlock(); 148 | } 149 | 150 | return result; 151 | } 152 | 153 | 154 | /** 155 | * 批量窃取节点,从尾部进行 156 | * @param taskArr 157 | * @return 158 | */ 159 | CBool trySteal(std::vector& taskArr, int maxStealBatchSize) { 160 | bool result = false; 161 | if (!deque_.empty() && lock_.try_lock()) { 162 | while (!deque_.empty() && maxStealBatchSize--) { 163 | taskArr.emplace_back(std::forward(deque_.back())); 164 | deque_.pop_back(); 165 | result = true; 166 | } 167 | lock_.unlock(); 168 | } 169 | 170 | return result; // 如果非空,表示盗取成功 171 | } 172 | 173 | UWorkStealingQueue() = default; 174 | 175 | CGRAPH_NO_ALLOWED_COPY(UWorkStealingQueue) 176 | 177 | private: 178 | std::deque deque_; // 存放任务的双向队列 179 | std::mutex lock_; // 用于处理deque_的锁 180 | }; 181 | 182 | CGRAPH_NAMESPACE_END 183 | 184 | #endif //CGRAPH_UWORKSTEALINGQUEUE_H 185 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Semaphore/USemaphore.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: USemaphore.h 5 | @Time: 2023/10/9 22:01 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_USEMAPHORE_H 10 | #define CGRAPH_USEMAPHORE_H 11 | 12 | CGRAPH_NAMESPACE_BEGIN 13 | 14 | #include 15 | #include 16 | 17 | #include "../UThreadObject.h" 18 | 19 | class USemaphore : public UThreadObject { 20 | public: 21 | /** 22 | * 触发一次信号 23 | */ 24 | CVoid signal() { 25 | CGRAPH_UNIQUE_LOCK lk(mutex_); 26 | cnt_++; 27 | if (cnt_ <= 0) { 28 | cv_.notify_one(); 29 | } 30 | } 31 | 32 | /** 33 | * 等待信号触发 34 | */ 35 | CVoid wait() { 36 | CGRAPH_UNIQUE_LOCK lk(mutex_); 37 | cnt_--; 38 | if (cnt_ < 0) { 39 | cv_.wait(lk); 40 | } 41 | } 42 | 43 | private: 44 | CInt cnt_ = 0; // 记录当前的次数 45 | std::mutex mutex_; 46 | std::condition_variable cv_; 47 | }; 48 | 49 | CGRAPH_NAMESPACE_END 50 | 51 | #endif //CGRAPH_USEMAPHORE_H 52 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Task/UTask.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UTask.h 5 | @Time: 2021/7/2 11:32 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTASK_H 10 | #define CGRAPH_UTASK_H 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "../UThreadObject.h" 17 | 18 | CGRAPH_NAMESPACE_BEGIN 19 | 20 | class UTask : public UThreadObject { 21 | struct taskBased { 22 | explicit taskBased() = default; 23 | virtual CVoid call() = 0; 24 | virtual ~taskBased() = default; 25 | }; 26 | 27 | // 退化以获得实际类型,修改思路参考:https://github.com/ChunelFeng/CThreadPool/pull/3 28 | template::type> 29 | struct taskDerided : taskBased { 30 | T func_; 31 | explicit taskDerided(F&& func) : func_(std::forward(func)) {} 32 | CVoid call() override { func_(); } 33 | }; 34 | 35 | public: 36 | template 37 | UTask(F&& f, int priority = 0) 38 | : impl_(new taskDerided(std::forward(f))) 39 | , priority_(priority) {} 40 | 41 | CVoid operator()() { 42 | impl_->call(); 43 | } 44 | 45 | UTask() = default; 46 | 47 | UTask(UTask&& task) noexcept: 48 | impl_(std::move(task.impl_)), 49 | priority_(task.priority_) {} 50 | 51 | UTask &operator=(UTask&& task) noexcept { 52 | impl_ = std::move(task.impl_); 53 | priority_ = task.priority_; 54 | return *this; 55 | } 56 | 57 | CBool operator>(const UTask& task) const { 58 | return priority_ < task.priority_; // 新加入的,放到后面 59 | } 60 | 61 | CBool operator<(const UTask& task) const { 62 | return priority_ >= task.priority_; 63 | } 64 | 65 | CGRAPH_NO_ALLOWED_COPY(UTask) 66 | 67 | private: 68 | std::unique_ptr impl_ = nullptr; 69 | int priority_ = 0; // 任务的优先级信息 70 | }; 71 | 72 | 73 | using UTaskRef = UTask &; 74 | using UTaskPtr = UTask *; 75 | using UTaskArr = std::vector; 76 | using UTaskArrRef = std::vector &; 77 | 78 | CGRAPH_NAMESPACE_END 79 | 80 | #endif //CGRAPH_UTASK_H 81 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Task/UTaskGroup.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UTaskGroup.h 5 | @Time: 2022/1/2 2:17 下午 6 | @Desc: 任务组,用于批量提交 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTASKGROUP_H 10 | #define CGRAPH_UTASKGROUP_H 11 | 12 | #include 13 | 14 | #include "../UThreadObject.h" 15 | 16 | CGRAPH_NAMESPACE_BEGIN 17 | 18 | class UTaskGroup : public UThreadObject { 19 | public: 20 | explicit UTaskGroup() = default; 21 | CGRAPH_NO_ALLOWED_COPY(UTaskGroup) 22 | 23 | /** 24 | * 直接通过函数来申明taskGroup 25 | * @param task 26 | * @param ttl 27 | * @param onFinished 28 | */ 29 | explicit UTaskGroup(CGRAPH_DEFAULT_CONST_FUNCTION_REF task, 30 | CMSec ttl = CGRAPH_MAX_BLOCK_TTL, 31 | CGRAPH_CALLBACK_CONST_FUNCTION_REF onFinished = nullptr) noexcept { 32 | this->addTask(task) 33 | ->setTtl(ttl) 34 | ->setOnFinished(onFinished); 35 | } 36 | 37 | /** 38 | * 添加一个任务 39 | * @param task 40 | */ 41 | UTaskGroup* addTask(CGRAPH_DEFAULT_CONST_FUNCTION_REF task) { 42 | task_arr_.emplace_back(task); 43 | return this; 44 | } 45 | 46 | /** 47 | * 设置任务最大超时时间 48 | * @param ttl 49 | */ 50 | UTaskGroup* setTtl(CMSec ttl) { 51 | this->ttl_ = ttl; 52 | return this; 53 | } 54 | 55 | /** 56 | * 设置执行完成后的回调函数 57 | * @param onFinished 58 | * @return 59 | */ 60 | UTaskGroup* setOnFinished(CGRAPH_CALLBACK_CONST_FUNCTION_REF onFinished) { 61 | this->on_finished_ = onFinished; 62 | return this; 63 | } 64 | 65 | /** 66 | * 获取最大超时时间信息 67 | * @return 68 | */ 69 | CMSec getTtl() const { 70 | return this->ttl_; 71 | } 72 | 73 | /** 74 | * 清空任务组 75 | */ 76 | CVoid clear() { 77 | task_arr_.clear(); 78 | } 79 | 80 | /** 81 | * 获取任务组大小 82 | * @return 83 | */ 84 | CSize getSize() const { 85 | auto size = task_arr_.size(); 86 | return size; 87 | } 88 | 89 | private: 90 | std::vector task_arr_; // 任务消息 91 | CMSec ttl_ = CGRAPH_MAX_BLOCK_TTL; // 任务组最大执行耗时(如果是0的话,则表示不阻塞) 92 | CGRAPH_CALLBACK_FUNCTION on_finished_ = nullptr; // 执行函数任务结束 93 | 94 | friend class UThreadPool; 95 | }; 96 | 97 | using UTaskGroupPtr = UTaskGroup *; 98 | using UTaskGroupRef = UTaskGroup &; 99 | 100 | CGRAPH_NAMESPACE_END 101 | 102 | #endif //CGRAPH_UTASKGROUP_H 103 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Task/UTaskInclude.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UTaskInclude.h 5 | @Time: 2022/1/12 9:34 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTASKINCLUDE_H 10 | #define CGRAPH_UTASKINCLUDE_H 11 | 12 | #include "UTask.h" 13 | #include "UTaskGroup.h" 14 | 15 | #endif //CGRAPH_UTASKINCLUDE_H 16 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Thread/UThreadBase.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UThreadBase.h 5 | @Time: 2021/7/2 11:24 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTHREADBASE_H 10 | #define CGRAPH_UTHREADBASE_H 11 | 12 | #include 13 | 14 | #include "../UThreadObject.h" 15 | #include "../Queue/UQueueInclude.h" 16 | #include "../Task/UTaskInclude.h" 17 | 18 | 19 | CGRAPH_NAMESPACE_BEGIN 20 | 21 | class UThreadBase : public UThreadObject { 22 | protected: 23 | explicit UThreadBase() { 24 | done_ = true; 25 | is_init_ = false; 26 | is_running_ = false; 27 | pool_task_queue_ = nullptr; 28 | pool_priority_task_queue_ = nullptr; 29 | config_ = nullptr; 30 | total_task_num_ = 0; 31 | } 32 | 33 | 34 | ~UThreadBase() override { 35 | reset(); 36 | } 37 | 38 | 39 | /** 40 | * 所有线程类的 destroy 函数应该是一样的 41 | * 但是init函数不一样,因为线程构造函数不同 42 | * @return 43 | */ 44 | CStatus destroy() override { 45 | CGRAPH_FUNCTION_BEGIN 46 | CGRAPH_ASSERT_INIT(true) 47 | 48 | reset(); 49 | CGRAPH_FUNCTION_END 50 | } 51 | 52 | 53 | /** 54 | * 从线程池的队列中,获取任务 55 | * @param task 56 | * @return 57 | */ 58 | virtual bool popPoolTask(UTaskRef task) { 59 | bool result = pool_task_queue_->tryPop(task); 60 | if (!result && CGRAPH_THREAD_TYPE_SECONDARY == type_) { 61 | // 如果辅助线程没有获取到的话,还需要再尝试从长时间任务队列中,获取一次 62 | result = pool_priority_task_queue_->tryPop(task); 63 | } 64 | return result; 65 | } 66 | 67 | 68 | /** 69 | * 从线程池的队列中中,获取批量任务 70 | * @param tasks 71 | * @return 72 | */ 73 | virtual bool popPoolTask(UTaskArrRef tasks) { 74 | bool result = pool_task_queue_->tryPop(tasks, config_->max_pool_batch_size_); 75 | if (!result && CGRAPH_THREAD_TYPE_SECONDARY == type_) { 76 | result = pool_priority_task_queue_->tryPop(tasks, 1); // 从优先队列里,最多pop出来一个 77 | } 78 | return result; 79 | } 80 | 81 | 82 | /** 83 | * 执行单个任务 84 | * @param task 85 | */ 86 | CVoid runTask(UTask& task) { 87 | is_running_ = true; 88 | task(); 89 | total_task_num_++; 90 | is_running_ = false; 91 | } 92 | 93 | 94 | /** 95 | * 批量执行任务 96 | * @param tasks 97 | */ 98 | CVoid runTasks(UTaskArr& tasks) { 99 | is_running_ = true; 100 | for (auto& task : tasks) { 101 | task(); 102 | } 103 | total_task_num_ += tasks.size(); 104 | is_running_ = false; 105 | } 106 | 107 | 108 | /** 109 | * 清空所有任务内容 110 | */ 111 | CVoid reset() { 112 | done_ = false; 113 | if (thread_.joinable()) { 114 | thread_.join(); // 等待线程结束 115 | } 116 | is_init_ = false; 117 | is_running_ = false; 118 | total_task_num_ = 0; 119 | } 120 | 121 | /** 122 | * 执行单个消息 123 | * @return 124 | */ 125 | virtual CVoid processTask() = 0; 126 | 127 | 128 | /** 129 | * 获取批量执行task信息 130 | */ 131 | virtual CVoid processTasks() = 0; 132 | 133 | 134 | /** 135 | * 循环处理任务 136 | * @return 137 | */ 138 | CStatus loopProcess() { 139 | CGRAPH_FUNCTION_BEGIN 140 | CGRAPH_ASSERT_NOT_NULL(config_) 141 | 142 | if (config_->batch_task_enable_) { 143 | while (done_) { 144 | processTasks(); // 批量任务获取执行接口 145 | } 146 | } else { 147 | while (done_) { 148 | processTask(); // 单个任务获取执行接口 149 | } 150 | } 151 | 152 | CGRAPH_FUNCTION_END 153 | } 154 | 155 | 156 | /** 157 | * 设置线程优先级,仅针对非windows平台使用 158 | */ 159 | CVoid setSchedParam() { 160 | #ifndef _WIN32 161 | int priority = CGRAPH_THREAD_SCHED_OTHER; 162 | int policy = CGRAPH_THREAD_MIN_PRIORITY; 163 | if (type_ == CGRAPH_THREAD_TYPE_PRIMARY) { 164 | priority = config_->primary_thread_priority_; 165 | policy = config_->primary_thread_policy_; 166 | } else if (type_ == CGRAPH_THREAD_TYPE_SECONDARY) { 167 | priority = config_->secondary_thread_priority_; 168 | policy = config_->secondary_thread_policy_; 169 | } 170 | 171 | auto handle = thread_.native_handle(); 172 | sched_param param = { calcPriority(priority) }; 173 | int ret = pthread_setschedparam(handle, calcPolicy(policy), ¶m); 174 | if (0 != ret) { 175 | CGRAPH_ECHO("warning : set thread sched param failed, system error code is [%d]", ret); 176 | } 177 | #endif 178 | } 179 | 180 | /** 181 | * 设置线程亲和性,仅针对linux系统 182 | */ 183 | CVoid setAffinity(int index) { 184 | #if defined(__linux__) && !defined(__ANDROID__) 185 | if (!config_->bind_cpu_enable_ || CGRAPH_CPU_NUM == 0 || index < 0) { 186 | return; 187 | } 188 | 189 | cpu_set_t mask; 190 | CPU_ZERO(&mask); 191 | CPU_SET(index % CGRAPH_CPU_NUM, &mask); 192 | 193 | auto handle = thread_.native_handle(); 194 | int ret = pthread_setaffinity_np(handle, sizeof(cpu_set_t), &mask); 195 | if (0 != ret) { 196 | CGRAPH_ECHO("warning : set thread affinity failed, system error code is [%d]", ret); 197 | } 198 | #endif 199 | } 200 | 201 | 202 | private: 203 | /** 204 | * 设定计算线程调度策略信息, 205 | * 非OTHER/RR/FIFO对应数值,统一返回OTHER类型 206 | * @param policy 207 | * @return 208 | */ 209 | static int calcPolicy(int policy) { 210 | return (CGRAPH_THREAD_SCHED_OTHER == policy 211 | || CGRAPH_THREAD_SCHED_RR == policy 212 | || CGRAPH_THREAD_SCHED_FIFO == policy) 213 | ? policy : CGRAPH_THREAD_SCHED_OTHER; 214 | } 215 | 216 | 217 | /** 218 | * 设定线程优先级信息 219 | * 超过[min,max]范围,统一设置为min值 220 | * @param priority 221 | * @return 222 | */ 223 | static int calcPriority(int priority) { 224 | return (priority >= CGRAPH_THREAD_MIN_PRIORITY 225 | && priority <= CGRAPH_THREAD_MAX_PRIORITY) 226 | ? priority : CGRAPH_THREAD_MIN_PRIORITY; 227 | } 228 | 229 | 230 | protected: 231 | bool done_; // 线程状态标记 232 | bool is_init_; // 标记初始化状态 233 | bool is_running_; // 是否正在执行 234 | int type_ = 0; // 用于区分线程类型(主线程、辅助线程) 235 | unsigned long total_task_num_ = 0; // 处理的任务的数字 236 | 237 | UAtomicQueue* pool_task_queue_; // 用于存放线程池中的普通任务 238 | UAtomicPriorityQueue* pool_priority_task_queue_; // 用于存放线程池中的包含优先级任务的队列,仅辅助线程可以执行 239 | UThreadPoolConfigPtr config_ = nullptr; // 配置参数信息 240 | std::thread thread_; // 线程类 241 | }; 242 | 243 | CGRAPH_NAMESPACE_END 244 | 245 | #endif //CGRAPH_UTHREADBASE_H 246 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Thread/UThreadInclude.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UThreadInclude.h 5 | @Time: 2022/1/12 11:09 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTHREADINCLUDE_H 10 | #define CGRAPH_UTHREADINCLUDE_H 11 | 12 | #include "UThreadPrimary.h" 13 | #include "UThreadSecondary.h" 14 | 15 | #endif //CGRAPH_UTHREADINCLUDE_H 16 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Thread/UThreadPrimary.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UThreadPrimary.h 5 | @Time: 2021/7/8 11:02 下午 6 | @Desc: 核心线程,处理任务中 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTHREADPRIMARY_H 10 | #define CGRAPH_UTHREADPRIMARY_H 11 | 12 | #include 13 | #include 14 | 15 | #include "UThreadBase.h" 16 | 17 | CGRAPH_NAMESPACE_BEGIN 18 | 19 | class UThreadPrimary : public UThreadBase { 20 | protected: 21 | explicit UThreadPrimary() { 22 | index_ = CGRAPH_SECONDARY_THREAD_COMMON_ID; 23 | pool_threads_ = nullptr; 24 | type_ = CGRAPH_THREAD_TYPE_PRIMARY; 25 | } 26 | 27 | 28 | CStatus init() override { 29 | CGRAPH_FUNCTION_BEGIN 30 | CGRAPH_ASSERT_INIT(false) 31 | CGRAPH_ASSERT_NOT_NULL(config_) 32 | 33 | is_init_ = true; 34 | buildStealTargets(); 35 | thread_ = std::move(std::thread(&UThreadPrimary::run, this)); 36 | setSchedParam(); 37 | setAffinity(index_); 38 | CGRAPH_FUNCTION_END 39 | } 40 | 41 | 42 | /** 43 | * 注册线程池相关内容,需要在init之前使用 44 | * @param index 45 | * @param poolTaskQueue 46 | * @param poolThreads 47 | * @param config 48 | */ 49 | CStatus setThreadPoolInfo(int index, 50 | UAtomicQueue* poolTaskQueue, 51 | std::vector* poolThreads, 52 | UThreadPoolConfigPtr config) { 53 | CGRAPH_FUNCTION_BEGIN 54 | CGRAPH_ASSERT_INIT(false) // 初始化之前,设置参数 55 | CGRAPH_ASSERT_NOT_NULL(poolTaskQueue, poolThreads, config) 56 | 57 | this->index_ = index; 58 | this->pool_task_queue_ = poolTaskQueue; 59 | this->pool_threads_ = poolThreads; 60 | this->config_ = config; 61 | CGRAPH_FUNCTION_END 62 | } 63 | 64 | 65 | /** 66 | * 线程执行函数 67 | * @return 68 | */ 69 | CStatus run() final { 70 | CGRAPH_FUNCTION_BEGIN 71 | CGRAPH_ASSERT_INIT(true) 72 | CGRAPH_ASSERT_NOT_NULL(pool_threads_) 73 | 74 | /** 75 | * 线程池中任何一个primary线程为null都不可以执行 76 | * 防止线程初始化失败的情况,导致的崩溃 77 | * 理论不会走到这个判断逻辑里面 78 | */ 79 | if (std::any_of(pool_threads_->begin(), pool_threads_->end(), 80 | [](UThreadPrimary* thd) { 81 | return nullptr == thd; 82 | })) { 83 | CGRAPH_RETURN_ERROR_STATUS("primary thread is null") 84 | } 85 | 86 | status = loopProcess(); 87 | CGRAPH_FUNCTION_END 88 | } 89 | 90 | 91 | CVoid processTask() override { 92 | UTask task; 93 | if (popTask(task) || popPoolTask(task) || stealTask(task)) { 94 | runTask(task); 95 | } else { 96 | fatWait(); 97 | } 98 | } 99 | 100 | 101 | CVoid processTasks() override { 102 | UTaskArr tasks; 103 | if (popTask(tasks) || popPoolTask(tasks) || stealTask(tasks)) { 104 | // 尝试从主线程中获取/盗取批量task,如果成功,则依次执行 105 | runTasks(tasks); 106 | } else { 107 | fatWait(); 108 | } 109 | } 110 | 111 | 112 | /** 113 | * 如果总是进入无task的状态,则开始休眠 114 | * 休眠一定时间后,然后恢复执行状态,避免出现 115 | */ 116 | CVoid fatWait() { 117 | cur_empty_epoch_++; 118 | std::this_thread::yield(); 119 | if (cur_empty_epoch_ >= config_->primary_thread_busy_epoch_) { 120 | CGRAPH_UNIQUE_LOCK lk(mutex_); 121 | cv_.wait_for(lk, std::chrono::milliseconds(config_->primary_thread_empty_interval_)); 122 | cur_empty_epoch_ = 0; 123 | } 124 | } 125 | 126 | 127 | /** 128 | * 依次push到任一队列里。如果都失败,则yield,然后重新push 129 | * @param task 130 | * @return 131 | */ 132 | CVoid pushTask(UTask&& task) { 133 | while (!(primary_queue_.tryPush(std::move(task)) 134 | || secondary_queue_.tryPush(std::move(task)))) { 135 | std::this_thread::yield(); 136 | } 137 | cv_.notify_one(); 138 | } 139 | 140 | 141 | /** 142 | * 从本地弹出一个任务 143 | * @param task 144 | * @return 145 | */ 146 | bool popTask(UTaskRef task) { 147 | return primary_queue_.tryPop(task) || secondary_queue_.tryPop(task); 148 | } 149 | 150 | 151 | /** 152 | * 从本地弹出一批任务 153 | * @param tasks 154 | * @return 155 | */ 156 | bool popTask(UTaskArrRef tasks) { 157 | CBool result = primary_queue_.tryPop(tasks, config_->max_local_batch_size_); 158 | auto leftSize = config_->max_local_batch_size_ - tasks.size(); 159 | if (leftSize > 0) { 160 | // 如果凑齐了,就不需要了。没凑齐的话,就继续 161 | result |= (secondary_queue_.tryPop(tasks, leftSize)); 162 | } 163 | return result; 164 | } 165 | 166 | 167 | /** 168 | * 从其他线程窃取一个任务 169 | * @param task 170 | * @return 171 | */ 172 | bool stealTask(UTaskRef task) { 173 | if (unlikely(pool_threads_->size() < config_->default_thread_size_)) { 174 | /** 175 | * 线程池还未初始化完毕的时候,无法进行steal。 176 | * 确保程序安全运行。 177 | */ 178 | return false; 179 | } 180 | 181 | /** 182 | * 窃取的时候,仅从相邻的primary线程中窃取 183 | * 待窃取相邻的数量,不能超过默认primary线程数 184 | */ 185 | for (auto& target : steal_targets_) { 186 | /** 187 | * 从线程中周围的thread中,窃取任务。 188 | * 如果成功,则返回true,并且执行任务。 189 | * steal 的时候,先从第二个队列里偷,从而降低触碰锁的概率 190 | */ 191 | if (likely((*pool_threads_)[target]) 192 | && (((*pool_threads_)[target])->secondary_queue_.trySteal(task)) 193 | || ((*pool_threads_)[target])->primary_queue_.trySteal(task)) { 194 | return true; 195 | } 196 | } 197 | 198 | return false; 199 | } 200 | 201 | 202 | /** 203 | * 从其他线程盗取一批任务 204 | * @param tasks 205 | * @return 206 | */ 207 | bool stealTask(UTaskArrRef tasks) { 208 | if (unlikely(pool_threads_->size() != config_->default_thread_size_)) { 209 | return false; 210 | } 211 | 212 | for (auto& target : steal_targets_) { 213 | if (likely((*pool_threads_)[target])) { 214 | bool result = ((*pool_threads_)[target])->secondary_queue_.trySteal(tasks, config_->max_steal_batch_size_); 215 | auto leftSize = config_->max_steal_batch_size_ - tasks.size(); 216 | if (leftSize > 0) { 217 | result |= ((*pool_threads_)[target])->primary_queue_.trySteal(tasks, leftSize); 218 | } 219 | 220 | if (result) { 221 | /** 222 | * 在这里,我们对模型进行了简化。实现的思路是: 223 | * 尝试从邻居主线程(先secondary,再primary)中,获取 x(=max_steal_batch_size_) 个task, 224 | * 如果从某一个邻居中,获取了 y(<=x) 个task,则也终止steal的流程 225 | * 且如果如果有一次批量steal成功,就认定成功 226 | */ 227 | return true; 228 | } 229 | } 230 | } 231 | 232 | return false; 233 | } 234 | 235 | 236 | /** 237 | * 构造 steal 范围的 target,避免每次盗取的时候,重复计算 238 | * @return 239 | */ 240 | CVoid buildStealTargets() { 241 | steal_targets_.clear(); 242 | for (int i = 0; i < config_->calcStealRange(); i++) { 243 | auto target = (index_ + i + 1) % config_->default_thread_size_; 244 | steal_targets_.push_back(target); 245 | } 246 | steal_targets_.shrink_to_fit(); 247 | } 248 | 249 | private: 250 | int index_; // 线程index 251 | int cur_empty_epoch_ = 0; // 当前空转的轮数信息 252 | UWorkStealingQueue primary_queue_; // 内部队列信息 253 | UWorkStealingQueue secondary_queue_; // 第二个队列,用于减少触锁概率,提升性能 254 | std::vector* pool_threads_; // 用于存放线程池中的线程信息 255 | std::vector steal_targets_; // 被偷的目标信息 256 | 257 | std::mutex mutex_; 258 | std::condition_variable cv_; 259 | 260 | friend class UThreadPool; 261 | friend class UAllocator; 262 | }; 263 | 264 | using UThreadPrimaryPtr = UThreadPrimary *; 265 | 266 | CGRAPH_NAMESPACE_END 267 | 268 | #endif //CGRAPH_UTHREADPRIMARY_H 269 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/Thread/UThreadSecondary.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UThreadSecondary.h 5 | @Time: 2021/7/8 11:02 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTHREADSECONDARY_H 10 | #define CGRAPH_UTHREADSECONDARY_H 11 | 12 | #include "UThreadBase.h" 13 | 14 | CGRAPH_NAMESPACE_BEGIN 15 | 16 | class UThreadSecondary : public UThreadBase { 17 | public: 18 | explicit UThreadSecondary() { 19 | cur_ttl_ = 0; 20 | type_ = CGRAPH_THREAD_TYPE_SECONDARY; 21 | } 22 | 23 | 24 | protected: 25 | CStatus init() override { 26 | CGRAPH_FUNCTION_BEGIN 27 | CGRAPH_ASSERT_INIT(false) 28 | CGRAPH_ASSERT_NOT_NULL(config_) 29 | 30 | cur_ttl_ = config_->secondary_thread_ttl_; 31 | is_init_ = true; 32 | thread_ = std::move(std::thread(&UThreadSecondary::run, this)); 33 | setSchedParam(); 34 | CGRAPH_FUNCTION_END 35 | } 36 | 37 | 38 | /** 39 | * 设置pool的信息 40 | * @param poolTaskQueue 41 | * @param poolPriorityTaskQueue 42 | * @param config 43 | * @return 44 | */ 45 | CStatus setThreadPoolInfo(UAtomicQueue* poolTaskQueue, 46 | UAtomicPriorityQueue* poolPriorityTaskQueue, 47 | UThreadPoolConfigPtr config) { 48 | CGRAPH_FUNCTION_BEGIN 49 | CGRAPH_ASSERT_INIT(false) // 初始化之前,设置参数 50 | CGRAPH_ASSERT_NOT_NULL(poolTaskQueue, poolPriorityTaskQueue, config) 51 | 52 | this->pool_task_queue_ = poolTaskQueue; 53 | this->pool_priority_task_queue_ = poolPriorityTaskQueue; 54 | this->config_ = config; 55 | CGRAPH_FUNCTION_END 56 | } 57 | 58 | 59 | CStatus run() final { 60 | CGRAPH_FUNCTION_BEGIN 61 | CGRAPH_ASSERT_INIT(true) 62 | 63 | status = loopProcess(); 64 | CGRAPH_FUNCTION_END 65 | } 66 | 67 | 68 | CVoid processTask() override { 69 | UTask task; 70 | if (popPoolTask(task)) { 71 | runTask(task); 72 | } else { 73 | // 如果单次无法获取,则稍加等待 74 | waitRunTask(config_->queue_emtpy_interval_); 75 | } 76 | } 77 | 78 | 79 | CVoid processTasks() override { 80 | UTaskArr tasks; 81 | if (popPoolTask(tasks)) { 82 | runTasks(tasks); 83 | } else { 84 | waitRunTask(config_->queue_emtpy_interval_); 85 | } 86 | } 87 | 88 | 89 | /** 90 | * 有等待的执行任务 91 | * @param ms 92 | * @return 93 | * @notice 目的是降低cpu的占用率 94 | */ 95 | CVoid waitRunTask(CMSec ms) { 96 | auto task = this->pool_task_queue_->popWithTimeout(ms); 97 | if (nullptr != task) { 98 | (*task)(); 99 | } 100 | } 101 | 102 | 103 | /** 104 | * 判断本线程是否需要被自动释放 105 | * @return 106 | */ 107 | bool freeze() { 108 | if (likely(is_running_)) { 109 | cur_ttl_++; 110 | cur_ttl_ = std::min(cur_ttl_, config_->secondary_thread_ttl_); 111 | } else { 112 | cur_ttl_--; // 如果当前线程没有在执行,则ttl-1 113 | } 114 | 115 | return cur_ttl_ <= 0 && done_; // 必须是正在执行的线程,才可以被回收 116 | } 117 | 118 | private: 119 | int cur_ttl_ = 0; // 当前最大生存周期 120 | 121 | friend class UThreadPool; 122 | }; 123 | 124 | using UThreadSecondaryPtr = UThreadSecondary *; 125 | 126 | CGRAPH_NAMESPACE_END 127 | 128 | #endif // CGRAPH_UTHREADSECONDARY_H 129 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/UThreadObject.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: CThreadObject.h 5 | @Time: 2021/7/2 10:39 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTHREADOBJECT_H 10 | #define CGRAPH_UTHREADOBJECT_H 11 | 12 | #include "../UtilsObject.h" 13 | #include "UThreadPoolDefine.h" 14 | 15 | CGRAPH_NAMESPACE_BEGIN 16 | 17 | class UThreadObject : public UtilsObject { 18 | protected: 19 | /** 20 | * 部分thread中的算子,可以不实现run方法 21 | * @return 22 | */ 23 | CStatus run() override { 24 | CGRAPH_NO_SUPPORT 25 | } 26 | }; 27 | 28 | CGRAPH_NAMESPACE_END 29 | 30 | #endif //CGRAPH_UTHREADOBJECT_H 31 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/UThreadPool.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UThreadPool.h 5 | @Time: 2021/7/4 1:34 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTHREADPOOL_H 10 | #define CGRAPH_UTHREADPOOL_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "UThreadObject.h" 22 | #include "UThreadPoolConfig.h" 23 | #include "Queue/UQueueInclude.h" 24 | #include "Thread/UThreadInclude.h" 25 | #include "Task/UTaskInclude.h" 26 | 27 | CGRAPH_NAMESPACE_BEGIN 28 | 29 | class UThreadPool : public UThreadObject { 30 | public: 31 | /** 32 | * 通过默认设置参数,来创建线程池 33 | * @param autoInit 是否自动开启线程池功能 34 | * @param config 35 | */ 36 | explicit UThreadPool(CBool autoInit = true, 37 | const UThreadPoolConfig& config = UThreadPoolConfig()) noexcept { 38 | cur_index_ = 0; 39 | is_init_ = false; 40 | this->setConfig(config); // setConfig 函数,用在 is_init_ 设定之后 41 | if (autoInit) { 42 | this->init(); 43 | } 44 | } 45 | 46 | /** 47 | * 析构函数 48 | */ 49 | ~UThreadPool() override { 50 | this->config_.monitor_enable_ = false; // 在析构的时候,才释放监控线程。先释放监控线程,再释放其他的线程 51 | if (monitor_thread_.joinable()) { 52 | monitor_thread_.join(); 53 | } 54 | 55 | destroy(); 56 | } 57 | 58 | /** 59 | * 设置线程池相关配置信息,需要在init()函数调用前,完成设置 60 | * @param config 61 | * @return 62 | * @notice 通过单例类(UThreadPoolSingleton)开启线程池,则线程池默认init。需要 destroy 后才可以设置参数 63 | */ 64 | CStatus setConfig(const UThreadPoolConfig &config) { 65 | CGRAPH_FUNCTION_BEGIN 66 | CGRAPH_ASSERT_INIT(false) // 初始化后,无法设置参数信息 67 | 68 | this->config_ = config; 69 | CGRAPH_FUNCTION_END 70 | } 71 | 72 | /** 73 | * 开启所有的线程信息 74 | * @return 75 | */ 76 | CStatus init() final { 77 | CGRAPH_FUNCTION_BEGIN 78 | if (is_init_) { 79 | CGRAPH_FUNCTION_END 80 | } 81 | 82 | monitor_thread_ = std::move(std::thread(&UThreadPool::monitor, this)); 83 | thread_record_map_.clear(); 84 | task_queue_.setup(); 85 | primary_threads_.reserve(config_.default_thread_size_); 86 | for (int i = 0; i < config_.default_thread_size_; i++) { 87 | auto ptr = CGRAPH_SAFE_MALLOC_COBJECT(UThreadPrimary); // 创建核心线程数 88 | ptr->setThreadPoolInfo(i, &task_queue_, &primary_threads_, &config_); 89 | 90 | // 记录线程和匹配id信息 91 | thread_record_map_[(CSize)std::hash{}(ptr->thread_.get_id())] = i; 92 | primary_threads_.emplace_back(ptr); 93 | } 94 | 95 | for (auto* pt : primary_threads_) { 96 | status += pt->init(); 97 | } 98 | CGRAPH_FUNCTION_CHECK_STATUS 99 | 100 | /** 101 | * 策略更新: 102 | * 初始化的时候,也可以创建n个辅助线程。目的是为了配合仅使用 pool中 priority_queue 的场景 103 | * 一般情况下,建议为0。 104 | */ 105 | status = createSecondaryThread(config_.secondary_thread_size_); 106 | CGRAPH_FUNCTION_CHECK_STATUS 107 | 108 | is_init_ = true; 109 | CGRAPH_FUNCTION_END 110 | } 111 | 112 | /** 113 | * 提交任务信息 114 | * @tparam FunctionType 115 | * @param func 116 | * @param index 117 | * @return 118 | */ 119 | template 120 | auto commit(const FunctionType& func, 121 | CIndex index = CGRAPH_DEFAULT_TASK_STRATEGY) 122 | -> std::future()())>; 123 | 124 | /** 125 | * 根据优先级,执行任务 126 | * @tparam FunctionType 127 | * @param func 128 | * @param priority 优先级别。自然序从大到小依次执行 129 | * @return 130 | * @notice 建议,priority 范围在 [-100, 100] 之间 131 | */ 132 | template 133 | auto commitWithPriority(const FunctionType& func, 134 | int priority) 135 | -> std::future()())>;; 136 | 137 | /** 138 | * 执行任务组信息 139 | * 取taskGroup内部ttl和入参ttl的最小值,为计算ttl标准 140 | * @param taskGroup 141 | * @param ttl 142 | * @return 143 | */ 144 | CStatus submit(const UTaskGroup& taskGroup, 145 | CMSec ttl = CGRAPH_MAX_BLOCK_TTL) { 146 | CGRAPH_FUNCTION_BEGIN 147 | CGRAPH_ASSERT_INIT(true) 148 | 149 | std::vector> futures; 150 | futures.reserve(taskGroup.task_arr_.size()); 151 | for (const auto& task : taskGroup.task_arr_) { 152 | futures.emplace_back(commit(task)); 153 | } 154 | 155 | // 计算最终运行时间信息 156 | auto deadline = std::chrono::steady_clock::now() 157 | + std::chrono::milliseconds(std::min(taskGroup.getTtl(), ttl)); 158 | 159 | for (auto& fut : futures) { 160 | const auto& futStatus = fut.wait_until(deadline); 161 | switch (futStatus) { 162 | case std::future_status::ready: break; // 正常情况,直接返回了 163 | case std::future_status::timeout: status += CErrStatus("thread status timeout"); break; 164 | case std::future_status::deferred: status += CErrStatus("thread status deferred"); break; 165 | default: status += CErrStatus("thread status unknown"); 166 | } 167 | } 168 | 169 | if (taskGroup.on_finished_) { 170 | taskGroup.on_finished_(status); 171 | } 172 | 173 | CGRAPH_FUNCTION_END 174 | } 175 | 176 | /** 177 | * 针对单个任务的情况,复用任务组信息,实现单个任务直接执行 178 | * @param task 179 | * @param ttl 180 | * @param onFinished 181 | * @return 182 | */ 183 | CStatus submit(CGRAPH_DEFAULT_CONST_FUNCTION_REF func, 184 | CMSec ttl = CGRAPH_MAX_BLOCK_TTL, 185 | CGRAPH_CALLBACK_CONST_FUNCTION_REF onFinished = nullptr) { 186 | return submit(UTaskGroup(func, ttl, onFinished)); 187 | } 188 | 189 | /** 190 | * 异步执行任务 191 | * @tparam FunctionType 192 | * @param task 193 | * @param index 194 | */ 195 | template 196 | void execute(const FunctionType& task, 197 | CIndex index = CGRAPH_DEFAULT_TASK_STRATEGY); 198 | 199 | /** 200 | * 获取根据线程id信息,获取线程index信息 201 | * @param tid 202 | * @return 203 | * @notice 辅助线程返回-1 204 | */ 205 | CIndex getThreadIndex(CSize tid) { 206 | int threadNum = CGRAPH_SECONDARY_THREAD_COMMON_ID; 207 | auto result = thread_record_map_.find(tid); 208 | if (result != thread_record_map_.end()) { 209 | threadNum = result->second; 210 | } 211 | 212 | return threadNum; 213 | } 214 | 215 | /** 216 | * 释放所有的线程信息 217 | * @return 218 | */ 219 | CStatus destroy() final { 220 | CGRAPH_FUNCTION_BEGIN 221 | if (!is_init_) { 222 | CGRAPH_FUNCTION_END 223 | } 224 | 225 | // primary 线程是普通指针,需要delete 226 | for (auto &pt : primary_threads_) { 227 | status += pt->destroy(); 228 | } 229 | CGRAPH_FUNCTION_CHECK_STATUS 230 | 231 | /** 232 | * 这里之所以 destroy和 delete分开两个循环执行, 233 | * 是因为当前线程被delete后,还可能存在未被delete的主线程,来steal当前线程的任务 234 | * 在windows环境下,可能出现问题。 235 | * destroy 和 delete 分开之后,不会出现此问题。 236 | * 感谢 Ryan大佬(https://github.com/ryanhuang) 提供的帮助 237 | */ 238 | for (auto &pt : primary_threads_) { 239 | CGRAPH_DELETE_PTR(pt) 240 | } 241 | primary_threads_.clear(); 242 | 243 | // secondary 线程是智能指针,不需要delete 244 | task_queue_.reset(); 245 | for (auto &st : secondary_threads_) { 246 | status += st->destroy(); 247 | } 248 | CGRAPH_FUNCTION_CHECK_STATUS 249 | secondary_threads_.clear(); 250 | thread_record_map_.clear(); 251 | is_init_ = false; 252 | 253 | CGRAPH_FUNCTION_END 254 | } 255 | 256 | /** 257 | * 判断线程池是否已经初始化了 258 | * @return 259 | */ 260 | CBool isInit() const { 261 | return is_init_; 262 | } 263 | 264 | /** 265 | * 生成辅助线程。内部确保辅助线程数量不超过设定参数 266 | * @param size 267 | * @return 268 | */ 269 | CStatus createSecondaryThread(CInt size) { 270 | CGRAPH_FUNCTION_BEGIN 271 | 272 | int leftSize = (int)(config_.max_thread_size_ - config_.default_thread_size_ - secondary_threads_.size()); 273 | int realSize = std::min(size, leftSize); // 使用 realSize 来确保所有的线程数量之和,不会超过设定max值 274 | 275 | CGRAPH_LOCK_GUARD lock(st_mutex_); 276 | for (int i = 0; i < realSize; i++) { 277 | auto ptr = CGRAPH_MAKE_UNIQUE_COBJECT(UThreadSecondary) 278 | ptr->setThreadPoolInfo(&task_queue_, &priority_task_queue_, &config_); 279 | status += ptr->init(); 280 | secondary_threads_.emplace_back(std::move(ptr)); 281 | } 282 | 283 | CGRAPH_FUNCTION_END 284 | } 285 | 286 | /** 287 | * 删除辅助线程 288 | * @param size 289 | * @return 290 | */ 291 | CStatus releaseSecondaryThread(CInt size) { 292 | CGRAPH_FUNCTION_BEGIN 293 | 294 | // 先将所有已经结束的,给删掉 295 | CGRAPH_LOCK_GUARD lock(st_mutex_); 296 | for (auto iter = secondary_threads_.begin(); iter != secondary_threads_.end(); ) { 297 | !(*iter)->done_ ? secondary_threads_.erase(iter++) : iter++; 298 | } 299 | 300 | CGRAPH_RETURN_ERROR_STATUS_BY_CONDITION((size > secondary_threads_.size()), \ 301 | "cannot release [" + std::to_string(size) + "] secondary thread," \ 302 | + "only [" + std::to_string(secondary_threads_.size()) + "] left.") 303 | 304 | // 再标记几个需要删除的信息 305 | for (auto iter = secondary_threads_.begin(); 306 | iter != secondary_threads_.end() && size-- > 0; ) { 307 | (*iter)->done_ = false; 308 | iter++; 309 | } 310 | CGRAPH_FUNCTION_END 311 | } 312 | 313 | protected: 314 | /** 315 | * 根据传入的策略信息,确定最终执行方式 316 | * @param origIndex 317 | * @return 318 | */ 319 | virtual CIndex dispatch(CIndex origIndex) { 320 | CIndex realIndex = 0; 321 | if (CGRAPH_DEFAULT_TASK_STRATEGY == origIndex) { 322 | realIndex = cur_index_++; 323 | if (cur_index_ >= config_.max_thread_size_ || cur_index_ < 0) { 324 | cur_index_ = 0; 325 | } 326 | } else { 327 | realIndex = origIndex; 328 | } 329 | 330 | return realIndex; // 交到上游去判断,走哪个线程 331 | } 332 | 333 | /** 334 | * 监控线程执行函数,主要是判断是否需要增加线程,或销毁线程 335 | * 增/删 操作,仅针对secondary类型线程生效 336 | */ 337 | CVoid monitor() { 338 | while (config_.monitor_enable_) { 339 | while (config_.monitor_enable_ && !is_init_) { 340 | // 如果没有init,则一直处于空跑状态 341 | CGRAPH_SLEEP_SECOND(1) 342 | } 343 | 344 | auto span = config_.monitor_span_; 345 | while (config_.monitor_enable_ && is_init_ && span--) { 346 | CGRAPH_SLEEP_SECOND(1) // 保证可以快速退出 347 | } 348 | 349 | // 如果 primary线程都在执行,则表示忙碌 350 | bool busy = !primary_threads_.empty() && std::all_of(primary_threads_.begin(), primary_threads_.end(), 351 | [](UThreadPrimaryPtr ptr) { return nullptr != ptr && ptr->is_running_; }); 352 | 353 | CGRAPH_LOCK_GUARD lock(st_mutex_); 354 | // 如果忙碌或者priority_task_queue_中有任务,则需要添加 secondary线程 355 | if (busy || !priority_task_queue_.empty()) { 356 | createSecondaryThread(1); 357 | } 358 | 359 | // 判断 secondary 线程是否需要退出 360 | for (auto iter = secondary_threads_.begin(); iter != secondary_threads_.end(); ) { 361 | (*iter)->freeze() ? secondary_threads_.erase(iter++) : iter++; 362 | } 363 | } 364 | } 365 | 366 | CGRAPH_NO_ALLOWED_COPY(UThreadPool) 367 | 368 | private: 369 | CBool is_init_ { false }; // 是否初始化 370 | CInt cur_index_ = 0; // 记录放入的线程数 371 | UAtomicQueue task_queue_; // 用于存放普通任务 372 | UAtomicPriorityQueue priority_task_queue_; // 运行时间较长的任务队列,仅在辅助线程中执行 373 | std::vector primary_threads_; // 记录所有的主线程 374 | std::list> secondary_threads_; // 用于记录所有的辅助线程 375 | UThreadPoolConfig config_; // 线程池设置值 376 | std::thread monitor_thread_; // 监控线程 377 | std::map thread_record_map_; // 线程记录的信息 378 | std::mutex st_mutex_; // 辅助线程发生变动的时候,加的mutex信息 379 | }; 380 | 381 | using UThreadPoolPtr = UThreadPool *; 382 | 383 | CGRAPH_NAMESPACE_END 384 | 385 | #include "UThreadPool.inl" 386 | 387 | #endif //CGRAPH_UTHREADPOOL_H 388 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/UThreadPool.inl: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UThreadPool.inl 5 | @Time: 2021/7/4 1:34 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTHREADPOOL_INL 10 | #define CGRAPH_UTHREADPOOL_INL 11 | 12 | #include "UThreadPool.h" 13 | 14 | CGRAPH_NAMESPACE_BEGIN 15 | 16 | template 17 | auto UThreadPool::commit(const FunctionType& func, CIndex index) 18 | -> std::future()())> { 19 | using ResultType = decltype(std::declval()()); 20 | 21 | std::packaged_task task(func); 22 | std::future result(task.get_future()); 23 | 24 | CIndex realIndex = dispatch(index); 25 | if (realIndex >= 0 && realIndex < config_.default_thread_size_) { 26 | // 如果返回的结果,在主线程数量之间,则放到主线程的queue中执行 27 | primary_threads_[realIndex]->pushTask(std::move(task)); 28 | } else if (CGRAPH_LONG_TIME_TASK_STRATEGY == realIndex) { 29 | /** 30 | * 如果是长时间任务,则交给特定的任务队列,仅由辅助线程处理 31 | * 目的是防止有很多长时间任务,将所有运行的线程均阻塞 32 | * 长任务程序,默认优先级较低 33 | **/ 34 | priority_task_queue_.push(std::move(task), CGRAPH_LONG_TIME_TASK_STRATEGY); 35 | } else { 36 | // 返回其他结果,放到pool的queue中执行 37 | task_queue_.push(std::move(task)); 38 | } 39 | return result; 40 | } 41 | 42 | 43 | template 44 | auto UThreadPool::commitWithPriority(const FunctionType& func, int priority) 45 | -> std::future()())> { 46 | using ResultType = decltype(std::declval()()); 47 | 48 | std::packaged_task task(func); 49 | std::future result(task.get_future()); 50 | 51 | if (secondary_threads_.empty()) { 52 | createSecondaryThread(1); // 如果没有开启辅助线程,则直接开启一个 53 | } 54 | 55 | priority_task_queue_.push(std::move(task), priority); 56 | return result; 57 | } 58 | 59 | 60 | template 61 | void UThreadPool::execute(const FunctionType& task, CIndex index) { 62 | CIndex realIndex = dispatch(index); 63 | if (realIndex >= 0 && realIndex < config_.default_thread_size_) { 64 | primary_threads_[realIndex]->pushTask(std::move(task)); 65 | } else if (CGRAPH_LONG_TIME_TASK_STRATEGY == realIndex) { 66 | priority_task_queue_.push(std::move(task), CGRAPH_LONG_TIME_TASK_STRATEGY); 67 | } else { 68 | // 返回其他结果,放到pool的queue中执行 69 | task_queue_.push(std::move(task)); 70 | } 71 | } 72 | 73 | CGRAPH_NAMESPACE_END 74 | 75 | #endif // CGRAPH_UTHREADPOOL_INL 76 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/UThreadPoolConfig.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UThreadPoolConfig.h 5 | @Time: 2022/1/3 9:31 下午 6 | @Desc: 线程池配置信息 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTHREADPOOLCONFIG_H 10 | #define CGRAPH_UTHREADPOOLCONFIG_H 11 | 12 | #include "UThreadObject.h" 13 | #include "UThreadPoolDefine.h" 14 | 15 | CGRAPH_NAMESPACE_BEGIN 16 | 17 | struct UThreadPoolConfig : public CStruct { 18 | /** 具体值含义,参考UThreadPoolDefine.h文件 */ 19 | int default_thread_size_ = CGRAPH_DEFAULT_THREAD_SIZE; 20 | int secondary_thread_size_ = CGRAPH_SECONDARY_THREAD_SIZE; 21 | int max_thread_size_ = CGRAPH_MAX_THREAD_SIZE; 22 | int max_task_steal_range_ = CGRAPH_MAX_TASK_STEAL_RANGE; 23 | int max_local_batch_size_ = CGRAPH_MAX_LOCAL_BATCH_SIZE; 24 | int max_pool_batch_size_ = CGRAPH_MAX_POOL_BATCH_SIZE; 25 | int max_steal_batch_size_ = CGRAPH_MAX_STEAL_BATCH_SIZE; 26 | int primary_thread_busy_epoch_ = CGRAPH_PRIMARY_THREAD_BUSY_EPOCH; 27 | int primary_thread_empty_interval_ = CGRAPH_PRIMARY_THREAD_EMPTY_INTERVAL; 28 | int secondary_thread_ttl_ = CGRAPH_SECONDARY_THREAD_TTL; 29 | CSec monitor_span_ = CGRAPH_MONITOR_SPAN; 30 | CMSec queue_emtpy_interval_ = CGRAPH_QUEUE_EMPTY_INTERVAL; 31 | int primary_thread_policy_ = CGRAPH_PRIMARY_THREAD_POLICY; 32 | int secondary_thread_policy_ = CGRAPH_SECONDARY_THREAD_POLICY; 33 | int primary_thread_priority_ = CGRAPH_PRIMARY_THREAD_PRIORITY; 34 | int secondary_thread_priority_ = CGRAPH_SECONDARY_THREAD_PRIORITY; 35 | bool bind_cpu_enable_ = CGRAPH_BIND_CPU_ENABLE; 36 | bool batch_task_enable_ = CGRAPH_BATCH_TASK_ENABLE; 37 | bool monitor_enable_ = CGRAPH_MONITOR_ENABLE; 38 | 39 | CStatus check() const { 40 | CGRAPH_FUNCTION_BEGIN 41 | if (default_thread_size_ < 0 || secondary_thread_size_ < 0) { 42 | CGRAPH_RETURN_ERROR_STATUS("thread size cannot less than 0") 43 | } 44 | 45 | if (default_thread_size_ + secondary_thread_size_ > max_thread_size_) { 46 | CGRAPH_RETURN_ERROR_STATUS("max thread size is less than default + secondary thread") 47 | } 48 | 49 | if (monitor_enable_ && monitor_span_ <= 0) { 50 | CGRAPH_RETURN_ERROR_STATUS("monitor span cannot less than 0") 51 | } 52 | CGRAPH_FUNCTION_END 53 | } 54 | 55 | protected: 56 | /** 57 | * 计算可盗取的范围,盗取范围不能超过默认线程数-1 58 | * @return 59 | */ 60 | int calcStealRange() const { 61 | int range = std::min(this->max_task_steal_range_, this->default_thread_size_ - 1); 62 | return range; 63 | } 64 | 65 | friend class UThreadPrimary; 66 | friend class UThreadSecondary; 67 | }; 68 | 69 | using UThreadPoolConfigPtr = UThreadPoolConfig *; 70 | 71 | CGRAPH_NAMESPACE_END 72 | 73 | #endif //CGRAPH_UTHREADPOOLCONFIG_H 74 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/UThreadPoolDefine.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: ThreadPoolDefine.h 5 | @Time: 2021/7/3 12:24 上午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTHREADPOOLDEFINE_H 10 | #define CGRAPH_UTHREADPOOLDEFINE_H 11 | 12 | #include 13 | #if __cplusplus >= 201703L 14 | #include 15 | #else 16 | # include 17 | #endif 18 | #include 19 | 20 | #include "../UtilsDefine.h" 21 | 22 | CGRAPH_NAMESPACE_BEGIN 23 | 24 | static const int CGRAPH_CPU_NUM = (int)std::thread::hardware_concurrency(); 25 | static const int CGRAPH_THREAD_TYPE_PRIMARY = 1; 26 | static const int CGRAPH_THREAD_TYPE_SECONDARY = 2; 27 | #ifndef _WIN32 28 | static const int CGRAPH_THREAD_SCHED_OTHER = SCHED_OTHER; 29 | static const int CGRAPH_THREAD_SCHED_RR = SCHED_RR; 30 | static const int CGRAPH_THREAD_SCHED_FIFO = SCHED_FIFO; 31 | #else 32 | /** 线程调度策略,暂不支持windows系统 */ 33 | static const int CGRAPH_THREAD_SCHED_OTHER = 0; 34 | static const int CGRAPH_THREAD_SCHED_RR = 0; 35 | static const int CGRAPH_THREAD_SCHED_FIFO = 0; 36 | #endif 37 | static const CInt CGRAPH_THREAD_MIN_PRIORITY = 0; // 线程最低优先级 38 | static const CInt CGRAPH_THREAD_MAX_PRIORITY = 99; // 线程最高优先级 39 | static const CMSec CGRAPH_MAX_BLOCK_TTL = 1999999999; // 最大阻塞时间,单位为ms 40 | static const CUint CGRAPH_DEFAULT_RINGBUFFER_SIZE = 1024; // 默认环形队列的大小 41 | static const CIndex CGRAPH_SECONDARY_THREAD_COMMON_ID = -1; // 辅助线程统一id标识 42 | static const CInt CGRAPH_DEFAULT_PRIORITY = 0; // 默认优先级 43 | 44 | 45 | static const int CGRAPH_DEFAULT_TASK_STRATEGY = -1; // 默认线程调度策略 46 | static const int CGRAPH_POOL_TASK_STRATEGY = -2; // 固定用pool中的队列的调度策略 47 | static const int CGRAPH_LONG_TIME_TASK_STRATEGY = -101; // 长时间任务调度策略 48 | 49 | /** 50 | * 以下为线程池配置信息 51 | */ 52 | static const int CGRAPH_DEFAULT_THREAD_SIZE = 8; // 默认开启主线程个数 53 | static const int CGRAPH_SECONDARY_THREAD_SIZE = 0; // 默认开启辅助线程个数 54 | static const int CGRAPH_MAX_THREAD_SIZE = 8; // 最大线程个数 55 | static const int CGRAPH_MAX_TASK_STEAL_RANGE = 7; // 盗取机制相邻范围 56 | static const bool CGRAPH_BATCH_TASK_ENABLE = false; // 是否开启批量任务功能 57 | static const int CGRAPH_MAX_LOCAL_BATCH_SIZE = 2; // 批量执行本地任务最大值 58 | static const int CGRAPH_MAX_POOL_BATCH_SIZE = 2; // 批量执行通用任务最大值 59 | static const int CGRAPH_MAX_STEAL_BATCH_SIZE = 2; // 批量盗取任务最大值 60 | static const int CGRAPH_PRIMARY_THREAD_BUSY_EPOCH = 10; // 主线程进入wait状态的轮数,数值越大,理论性能越高,但空转可能性也越大 61 | static const CMSec CGRAPH_PRIMARY_THREAD_EMPTY_INTERVAL = 1000; // 主线程进入休眠状态的默认时间 62 | static const int CGRAPH_SECONDARY_THREAD_TTL = 10; // 辅助线程ttl,单位为s 63 | static const bool CGRAPH_MONITOR_ENABLE = false; // 是否开启监控程序 64 | static const CSec CGRAPH_MONITOR_SPAN = 5; // 监控线程执行间隔,单位为s 65 | static const CMSec CGRAPH_QUEUE_EMPTY_INTERVAL = 1000; // 队列为空时,等待的时间。仅针对辅助线程,单位为ms 66 | static const bool CGRAPH_BIND_CPU_ENABLE = false; // 是否开启绑定cpu模式(仅针对主线程) 67 | static const int CGRAPH_PRIMARY_THREAD_POLICY = CGRAPH_THREAD_SCHED_OTHER; // 主线程调度策略 68 | static const int CGRAPH_SECONDARY_THREAD_POLICY = CGRAPH_THREAD_SCHED_OTHER; // 辅助线程调度策略 69 | static const int CGRAPH_PRIMARY_THREAD_PRIORITY = CGRAPH_THREAD_MIN_PRIORITY; // 主线程调度优先级(取值范围0~99,配合调度策略一起使用,不建议不了解相关内容的童鞋做修改) 70 | static const int CGRAPH_SECONDARY_THREAD_PRIORITY = CGRAPH_THREAD_MIN_PRIORITY; // 辅助线程调度优先级(同上) 71 | 72 | CGRAPH_NAMESPACE_END 73 | 74 | #endif // CGRAPH_UTHREADPOOLDEFINE_H 75 | -------------------------------------------------------------------------------- /src/UtilsCtrl/ThreadPool/UThreadPoolInclude.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: ThreadPoolInclude.h 5 | @Time: 2021/7/3 12:25 上午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTHREADPOOLINCLUDE_H 10 | #define CGRAPH_UTHREADPOOLINCLUDE_H 11 | 12 | #include "UThreadObject.h" 13 | #include "UThreadPool.h" 14 | #include "UThreadPoolDefine.h" 15 | #include "UThreadPoolConfig.h" 16 | #include "Queue/UQueueInclude.h" 17 | #include "Task/UTaskInclude.h" 18 | #include "Thread/UThreadInclude.h" 19 | #include "Lock/ULockInclude.h" 20 | #include "Semaphore/USemaphore.h" 21 | 22 | #endif //CGRAPH_UTHREADPOOLINCLUDE_H 23 | -------------------------------------------------------------------------------- /src/UtilsCtrl/UAllocator.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UAllocator.h 5 | @Time: 2021/10/28 9:15 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UALLOCATOR_H 10 | #define CGRAPH_UALLOCATOR_H 11 | 12 | #include 13 | #include 14 | 15 | #include "../CBasic/CBasicInclude.h" 16 | 17 | CGRAPH_NAMESPACE_BEGIN 18 | 19 | /** 20 | * 仅用于生成CObject类型的类 21 | */ 22 | class UAllocator : public CObject { 23 | public: 24 | /** 25 | * 生成一个 CObject 对象 26 | * @tparam T 27 | * @return 28 | */ 29 | template::value, int> = 0> 31 | static T* safeMallocCObject() { 32 | return safeMalloc(); 33 | } 34 | 35 | /** 36 | * 生成一个 CStruct 的对象 37 | * @tparam T 38 | * @return 39 | */ 40 | template::value, int> = 0> 42 | static T* safeMallocCStruct() { 43 | return safeMalloc(); 44 | } 45 | 46 | /** 47 | * 生成带参数的普通指针 48 | * @tparam T 49 | * @tparam Args 50 | * @param args 51 | * @return 52 | */ 53 | template::value, int> = 0> 55 | static T* safeMallocTemplateCObject(Args... args) { 56 | T* ptr = nullptr; 57 | while (!ptr) { 58 | ptr = new T(std::forward(args)...); 59 | } 60 | return ptr; 61 | } 62 | 63 | 64 | /** 65 | * 生成unique智能指针信息 66 | * @tparam T 67 | * @return 68 | */ 69 | template::value, int> = 0> 71 | static std::unique_ptr makeUniqueCObject() { 72 | return c_make_unique(); 73 | } 74 | 75 | private: 76 | /** 77 | * 生成T类型的对象 78 | * @tparam T 79 | * @return 80 | */ 81 | template 82 | static T* safeMalloc() { 83 | T* ptr = nullptr; 84 | while (!ptr) { 85 | ptr = new(std::nothrow) T(); 86 | } 87 | return ptr; 88 | } 89 | }; 90 | 91 | 92 | #define CGRAPH_SAFE_MALLOC_COBJECT(Type) \ 93 | UAllocator::safeMallocCObject(); \ 94 | 95 | #define CGRAPH_MAKE_UNIQUE_COBJECT(Type) \ 96 | UAllocator::makeUniqueCObject(); \ 97 | 98 | CGRAPH_NAMESPACE_END 99 | 100 | #endif //CGRAPH_UALLOCATOR_H 101 | -------------------------------------------------------------------------------- /src/UtilsCtrl/UtilsDefine.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UtilsDefine.h 5 | @Time: 2021/4/30 8:52 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTILSDEFINE_H 10 | #define CGRAPH_UTILSDEFINE_H 11 | 12 | #include 13 | #include 14 | 15 | #if __cplusplus >= 201703L 16 | #include 17 | #else 18 | #include 19 | #endif 20 | 21 | #include "../CBasic/CBasicInclude.h" 22 | #include "UAllocator.h" 23 | #include "UtilsFunction.h" 24 | 25 | CGRAPH_NAMESPACE_BEGIN 26 | 27 | #ifdef _ENABLE_LIKELY_ 28 | #define likely(x) __builtin_expect(!!(x), 1) 29 | #define unlikely(x) __builtin_expect(!!(x), 0) 30 | #else 31 | #define likely 32 | #define unlikely 33 | #endif 34 | 35 | using CGRAPH_LOCK_GUARD = std::lock_guard; 36 | using CGRAPH_UNIQUE_LOCK = std::unique_lock; 37 | 38 | #if __cplusplus >= 201703L 39 | using CGRAPH_READ_LOCK = std::shared_lock; 40 | using CGRAPH_WRITE_LOCK = std::unique_lock; 41 | #else 42 | using CGRAPH_READ_LOCK = CGRAPH_LOCK_GUARD; // C++14不支持读写锁,使用mutex替代 43 | using CGRAPH_WRITE_LOCK = CGRAPH_LOCK_GUARD; 44 | #endif 45 | 46 | 47 | template 48 | CStatus __ASSERT_NOT_NULL(T t) { 49 | return (unlikely(nullptr == t)) 50 | ? CErrStatus(CGRAPH_INPUT_IS_NULL) 51 | : CStatus(); 52 | } 53 | 54 | template 55 | CStatus __ASSERT_NOT_NULL(T t, Args... args) { 56 | if (unlikely(t == nullptr)) { 57 | return __ASSERT_NOT_NULL(t); 58 | } 59 | 60 | return __ASSERT_NOT_NULL(args...); 61 | } 62 | 63 | template 64 | CVoid __ASSERT_NOT_NULL_THROW_EXCEPTION(T t) { 65 | if (unlikely(nullptr == t)) { 66 | CGRAPH_THROW_EXCEPTION("[CException] " + CGRAPH_INPUT_IS_NULL) 67 | } 68 | } 69 | 70 | template 71 | CVoid __ASSERT_NOT_NULL_THROW_EXCEPTION(T t, Args... args) { 72 | if (unlikely(nullptr == t)) { 73 | __ASSERT_NOT_NULL_THROW_EXCEPTION(t); 74 | } 75 | 76 | __ASSERT_NOT_NULL_THROW_EXCEPTION(args...); 77 | } 78 | 79 | 80 | /** 判断传入的多个指针信息,是否为空 */ 81 | #define CGRAPH_ASSERT_NOT_NULL(ptr, ...) \ 82 | { \ 83 | const CStatus& __cur_status__ = __ASSERT_NOT_NULL(ptr, ##__VA_ARGS__); \ 84 | if (unlikely(__cur_status__.isErr())) { return __cur_status__; } \ 85 | } \ 86 | 87 | 88 | /** 判断传入的多个指针,是否为空。如果为空,则抛出异常信息 */ 89 | #define CGRAPH_ASSERT_NOT_NULL_THROW_ERROR(ptr, ...) \ 90 | __ASSERT_NOT_NULL_THROW_EXCEPTION(ptr, ##__VA_ARGS__); \ 91 | 92 | 93 | /* 判断函数流程是否可以继续 */ 94 | static std::mutex g_check_status_mtx; 95 | #define CGRAPH_FUNCTION_CHECK_STATUS \ 96 | if (unlikely(status.isErr())) { \ 97 | if (status.isCrash()) { throw CException(status.getInfo()); } \ 98 | CGRAPH_LOCK_GUARD lock{ g_check_status_mtx }; \ 99 | CGRAPH_ECHO("%s, errorCode = [%d], errorInfo = [%s].", \ 100 | status.getLocate().c_str(), status.getCode(), status.getInfo().c_str()); \ 101 | return status; \ 102 | } \ 103 | 104 | /* 删除资源信息 */ 105 | #define CGRAPH_DELETE_PTR(ptr) \ 106 | if (unlikely((ptr) != nullptr)) { \ 107 | delete (ptr); \ 108 | (ptr) = nullptr; \ 109 | } \ 110 | 111 | #define CGRAPH_ASSERT_INIT(isInit) \ 112 | if (unlikely((isInit) != is_init_)) { \ 113 | CGRAPH_RETURN_ERROR_STATUS("init status is not suitable") \ 114 | } \ 115 | 116 | #define CGRAPH_ASSERT_INIT_THROW_ERROR(isInit) \ 117 | if (unlikely((isInit) != is_init_)) { \ 118 | CGRAPH_THROW_EXCEPTION("[CException] init status is not suitable") \ 119 | } \ 120 | 121 | 122 | #define CGRAPH_CHECK_STATUS_RETURN_THIS_OR_NULL \ 123 | return status.isOK() ? this : nullptr; \ 124 | 125 | #define CGRAPH_SLEEP_SECOND(s) \ 126 | std::this_thread::sleep_for(std::chrono::seconds(s)); \ 127 | 128 | #define CGRAPH_SLEEP_MILLISECOND(ms) \ 129 | std::this_thread::sleep_for(std::chrono::milliseconds(ms)); \ 130 | 131 | CGRAPH_NAMESPACE_END 132 | 133 | #endif //CGRAPH_UTILSDEFINE_H 134 | -------------------------------------------------------------------------------- /src/UtilsCtrl/UtilsFunction.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UtilsFunction.h 5 | @Time: 2022/1/26 11:27 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTILSFUNCTION_H 10 | #define CGRAPH_UTILSFUNCTION_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "../CBasic/CBasicInclude.h" 20 | 21 | CGRAPH_NAMESPACE_BEGIN 22 | 23 | /** 24 | * 定制化输出 25 | * @param cmd 26 | * @param ... 27 | * 注:内部包含全局锁,不建议正式上线的时候使用 28 | */ 29 | static std::mutex g_echo_mtx; 30 | inline CVoid CGRAPH_ECHO(const char *cmd, ...) { 31 | #ifdef _CGRAPH_SILENCE_ 32 | return; 33 | #endif 34 | 35 | std::lock_guard lock{ g_echo_mtx }; 36 | auto now = std::chrono::system_clock::now(); 37 | auto time = std::chrono::system_clock::to_time_t(now); 38 | auto ms = std::chrono::duration_cast(now.time_since_epoch()).count() % 1000; 39 | std::cout << "[" << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S.") \ 40 | << std::setfill('0') << std::setw(3) << ms << "] "; 41 | 42 | va_list args; 43 | va_start(args, cmd); 44 | vprintf(cmd, args); 45 | va_end(args); 46 | std::cout << "\n"; 47 | } 48 | 49 | 50 | /** 51 | * 获取当前的ms信息 52 | * @return 53 | */ 54 | inline CMSec CGRAPH_GET_CURRENT_MS() { 55 | // 获取当前的时间戳信息 56 | return (CMSec)std::chrono::time_point_cast \ 57 | (std::chrono::steady_clock::now()).time_since_epoch().count(); 58 | } 59 | 60 | 61 | /** 62 | * 获取当前的ms信息(包含小数) 63 | * @return 64 | */ 65 | inline CFMSec CGRAPH_GET_CURRENT_ACCURATE_MS() { 66 | // 获取当前的时间戳信息 67 | return (CFMSec)std::chrono::time_point_cast \ 68 | (std::chrono::steady_clock::now()).time_since_epoch().count() / (CFMSec)1000.0; 69 | } 70 | 71 | 72 | /** 73 | * 通用容器累加信息 74 | * @tparam T (例:std::vector) 75 | * @param container 76 | * @return 77 | */ 78 | template 79 | typename T::value_type CGRAPH_CONTAINER_SUM(const T& container) { 80 | typename T::value_type result = 0; 81 | for (const auto& val : container) { 82 | result += val; 83 | } 84 | return result; 85 | } 86 | 87 | 88 | /** 89 | * 通用容器累乘信息 90 | * @tparam T (例:std::vector) 91 | * @param container 92 | * @return 93 | */ 94 | template 95 | typename T::value_type CGRAPH_CONTAINER_MULTIPLY(const T& container) { 96 | typename T::value_type result = 1; 97 | for (const auto& val : container) { 98 | result *= val; 99 | } 100 | return result; 101 | } 102 | 103 | 104 | /** 105 | * 获取max值 106 | * @tparam T 107 | * @param value 108 | * @return 109 | */ 110 | template 111 | T CGRAPH_MAX(T val) { 112 | return val; 113 | } 114 | 115 | template 116 | T CGRAPH_MAX(T val, Args... args) { 117 | return std::max(val, CGRAPH_MAX(args...)); 118 | } 119 | 120 | 121 | /** 122 | * 累加 123 | * @tparam T 124 | * @param t 125 | * @return 126 | */ 127 | template 128 | T CGRAPH_SUM(T t) { 129 | return t; 130 | } 131 | 132 | template 133 | T CGRAPH_SUM(T val, Args... args) { 134 | return val + CGRAPH_SUM(args...); 135 | } 136 | 137 | CGRAPH_NAMESPACE_END 138 | 139 | #endif //CGRAPH_UTILSFUNCTION_H 140 | -------------------------------------------------------------------------------- /src/UtilsCtrl/UtilsInclude.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UtilsInclude.h 5 | @Time: 2021/4/30 9:14 下午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTILSINCLUDE_H 10 | #define CGRAPH_UTILSINCLUDE_H 11 | 12 | #include "UtilsDefine.h" 13 | #include "UAllocator.h" 14 | #include "ThreadPool/UThreadPoolInclude.h" 15 | 16 | #endif //CGRAPH_UTILSINCLUDE_H 17 | -------------------------------------------------------------------------------- /src/UtilsCtrl/UtilsObject.h: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: UtilsObject.h 5 | @Time: 2021/9/19 12:00 上午 6 | @Desc: 7 | ***************************/ 8 | 9 | #ifndef CGRAPH_UTILSOBJECT_H 10 | #define CGRAPH_UTILSOBJECT_H 11 | 12 | #include "UtilsDefine.h" 13 | 14 | CGRAPH_NAMESPACE_BEGIN 15 | 16 | class UtilsObject : public CObject { 17 | public: 18 | CStatus run() override { 19 | CGRAPH_NO_SUPPORT 20 | } 21 | }; 22 | 23 | CGRAPH_NAMESPACE_END 24 | 25 | #endif //CGRAPH_UTILSOBJECT_H 26 | -------------------------------------------------------------------------------- /tutorial.cpp: -------------------------------------------------------------------------------- 1 | /*************************** 2 | @Author: Chunel 3 | @Contact: chunel@foxmail.com 4 | @File: main.cpp 5 | @Time: 2021/9/2 11:12 下午 6 | @Desc: 本例主要演示,threadpool工具的使用方法 7 | ***************************/ 8 | 9 | #include "src/CThreadPool.h" 10 | 11 | #include "MyFunction.h" 12 | 13 | using namespace CTP; 14 | 15 | void tutorial_threadpool_1(UThreadPoolPtr tp) { 16 | /** 17 | * 依次向线程池中传入: 18 | * 1、普通函数 19 | * 2、静态函数 20 | * 3、类成员函数 21 | * 4、类成员静态函数 22 | */ 23 | int i = 6, j = 3; 24 | std::string str = "5"; 25 | MyFunction mf; 26 | 27 | auto r1 = tp->commit([i, j] { return add(i, j); }); // 可以通过lambda表达式传递函数 28 | std::future r2 = tp->commit(std::bind(minusBy5, 8.5f)); // 可以传入任意个数的入参 29 | auto r3 = tp->commit(std::bind(&MyFunction::concat, mf, str)); // 返回值可以是任意类型 30 | std::future r4 = tp->commit([i, j] { return MyFunction::multiply(i, j); }); // 返回值实际上是std::future类型 31 | 32 | std::cout << r1.get() << std::endl; // 返回值可以是int类型 33 | std::cout << r2.get() << std::endl; // 等待r2对应函数执行完毕后,再继续执行。不调用get()为不等待 34 | std::cout << r3.get() << std::endl; // 返回值也可是string或其他任意类型 35 | std::cout << r4.get() << std::endl; 36 | } 37 | 38 | 39 | void tutorial_threadpool_2(UThreadPoolPtr tp) { 40 | /** 41 | * 通过添加工作组(taskGroup)来执行任务 42 | */ 43 | UTaskGroup taskGroup; 44 | 45 | /** 添加一个不耗时的任务 */ 46 | int i = 1, j = 2, k = 3; 47 | auto hcg = [] { CGRAPH_ECHO("Hello, CGraph."); }; 48 | taskGroup.addTask(hcg); 49 | 50 | /** 添加一个耗时为1000ms的任务 */ 51 | taskGroup.addTask([i, j] { 52 | int result = i + j; 53 | CGRAPH_SLEEP_MILLISECOND(1000) 54 | CGRAPH_ECHO("sleep for 1 second, [%d] + [%d] = [%d], run success.", i, j, result); 55 | }); 56 | 57 | taskGroup.addTask([i, j, k] { 58 | int result = i - j + k; 59 | CGRAPH_SLEEP_MILLISECOND(2000) 60 | CGRAPH_ECHO("sleep for 2 second, [%d] - [%d] + [%d] = [%d], run success.", i, j, k, result); 61 | return result; // submit接口,不会对线程函数返回值进行判断。如果需要判断,考虑commit方式 62 | }); 63 | 64 | /** 如果添加耗时3000ms的任务,则整体执行失败 */ 65 | /* taskGroup.addTask([] { 66 | CGRAPH_SLEEP_MILLISECOND(3000) 67 | }); */ 68 | 69 | /** 70 | * 可以添加执行task group结束后的回调信息 71 | * 其中sts是task group整体执行结果的返回值信息 72 | * */ 73 | /* taskGroup.setOnFinished([] (const CStatus& sts) { 74 | if(sts.isOK()) { 75 | CGRAPH_ECHO("task group run success."); 76 | } else { 77 | CGRAPH_ECHO("task group run failed, error info is [%s].", sts.getInfo().c_str()); 78 | } 79 | }); */ 80 | 81 | /** 82 | * 设定超时时间=2500ms,确保以上任务能顺利执行完成 83 | * 如果加入sleep(3000)的任务,则也会在2500ms的时候退出 84 | * 并且在status中提示超时信息 85 | * */ 86 | CStatus status = tp->submit(taskGroup, 2500); 87 | CGRAPH_ECHO("task group run status is [%d].", status.getCode()); 88 | } 89 | 90 | 91 | void tutorial_threadpool_3(UThreadPoolPtr tp) { 92 | /** 93 | * 并发打印0~100之间的数字 94 | * 使用commit和submit函数的区别,主要在于: 95 | * 1,commit()属于非阻塞执行,是将线程函数执行的结果以future的类型返回,交由上层处理 96 | * 2,submit()属于阻塞顺序执行,是在内部处理好超时等信息并作为结果返回,抛弃线程函数自身返回值 97 | * 3,不需要线程函数返回值,并且不需要判断超时信息的场景,两者无区别(如下例) 98 | */ 99 | const int size = 100; 100 | CGRAPH_ECHO("thread pool task submit version : "); 101 | for (int i = 0; i < size; i++) { 102 | tp->submit([i] { std::cout << i << " "; }); // 可以看到,submit版本是有序执行的。如果需要想要无序执行,可以通过创建taskGroup的方式进行,或者使用commit方法 103 | } 104 | CGRAPH_SLEEP_SECOND(1) // 等待上面函数执行完毕,以便于观察结果。无实际意义 105 | std::cout << "\r\n"; 106 | 107 | CGRAPH_ECHO("thread pool task group submit version : "); 108 | UTaskGroup taskGroup; 109 | for (int i = 0; i < size; i++) { 110 | taskGroup.addTask([i] { std::cout << i << " "; }); // 将任务放到一个taskGroup中,并发执行。执行的结果是无序的 111 | } 112 | tp->submit(taskGroup); 113 | CGRAPH_SLEEP_SECOND(1) 114 | std::cout << "\r\n"; 115 | 116 | CGRAPH_ECHO("thread pool task commit version : "); 117 | for (int i = 0; i < size; i++) { 118 | tp->commit([i] { std::cout << i << " "; }); // commit版本,是无序执行的 119 | } 120 | CGRAPH_SLEEP_SECOND(1) 121 | std::cout << "\r\n"; 122 | } 123 | 124 | 125 | int main() { 126 | std::unique_ptr pool(new UThreadPool()); // 构造一个线程池类的智能指针 127 | CGRAPH_ECHO("======== tutorial_threadpool_1 begin. ========"); 128 | tutorial_threadpool_1(pool.get()); 129 | 130 | CGRAPH_ECHO("======== tutorial_threadpool_2 begin. ========"); 131 | tutorial_threadpool_2(pool.get()); 132 | 133 | CGRAPH_ECHO("======== tutorial_threadpool_3 begin. ========"); 134 | tutorial_threadpool_3(pool.get()); 135 | return 0; 136 | } 137 | --------------------------------------------------------------------------------