├── clean.sh ├── .gitignore ├── README.md ├── experiment ├── a.out ├── test.cc ├── test_type_traits.cc ├── type_rich_test.cc ├── output_container.h └── thread_pool.cc ├── .clang-format ├── toolchain.cmake ├── singleton ├── test │ └── test.cc ├── CMakeLists.txt └── include │ └── singleton │ └── singleton.h ├── memory ├── MemoryDetect.h ├── test.cc └── MemoryDetect.cc ├── thread ├── CMakeLists.txt ├── include │ └── thread │ │ ├── count_down_latch.h │ │ └── thread_pool.h ├── src │ └── count_down_latch.cc └── test │ └── test.cc ├── common └── include │ └── common │ ├── noncopyable.h │ ├── defer.h │ ├── own_strings.h │ ├── cmd.h │ └── map.h ├── CMakeLists.txt ├── test.cc └── timer └── include └── timer └── timer.h /clean.sh: -------------------------------------------------------------------------------- 1 | rm -rf build/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wzq_utils 2 | c++工具库 3 | -------------------------------------------------------------------------------- /experiment/a.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chengxumiaodaren/wzq_utils/HEAD/experiment/a.out -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | IndentWidth: 4 3 | ColumnLimit: 120 4 | SortIncludes: true 5 | MaxEmptyLinesToKeep: 2 -------------------------------------------------------------------------------- /toolchain.cmake: -------------------------------------------------------------------------------- 1 | set (CMAKE_SYSTEM_NAME Linux) 2 | set (CMAKE_CXX_COMPILER /usr/bin/g++) 3 | set (CMAKE_C_COMPILER /usr/bin/gcc) -------------------------------------------------------------------------------- /experiment/test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | std::shared_ptr a; 7 | } -------------------------------------------------------------------------------- /singleton/test/test.cc: -------------------------------------------------------------------------------- 1 | #include "singleton/singleton.h" 2 | 3 | int main() { 4 | std::cout << "hello world" << std::endl; 5 | return 0; 6 | } -------------------------------------------------------------------------------- /singleton/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.0) 2 | project(wzq_singleton) 3 | 4 | set (CMAKE_CXX_FLAGS "--std=c++17") 5 | 6 | add_executable(test_singleton test/test.cc) -------------------------------------------------------------------------------- /memory/MemoryDetect.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | void* operator new(std::size_t size, const char* file, int line); 8 | void* operator new[](std::size_t size, const char* file, int line); 9 | 10 | #define new new (__FILE__, __LINE__) 11 | 12 | int checkLeaks(); 13 | -------------------------------------------------------------------------------- /thread/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.0) 2 | project(wzq_thread) 3 | 4 | set (CMAKE_CXX_FLAGS "--std=c++17") 5 | 6 | add_library(wzq_thread src/count_down_latch.cc) 7 | target_link_libraries(wzq_thread pthread) 8 | 9 | add_executable(test_thread test/test.cc) 10 | target_link_libraries(test_thread wzq_thread) -------------------------------------------------------------------------------- /common/include/common/noncopyable.h: -------------------------------------------------------------------------------- 1 | #ifndef __NONCOPYABLE__ 2 | #define __NONCOPYABLE__ 3 | 4 | #include 5 | 6 | namespace wzq { 7 | 8 | class NonCopyAble { 9 | public: 10 | NonCopyAble(const NonCopyAble&) = delete; 11 | NonCopyAble& operator=(const NonCopyAble&) = delete; 12 | 13 | protected: // 禁止多态调用时delete 基类指针 14 | NonCopyAble() = default; 15 | ~NonCopyAble() = default; 16 | }; 17 | 18 | } // namespace wzq 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /memory/test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "MemoryDetect.h" 6 | 7 | struct A { 8 | int a; 9 | }; 10 | 11 | int main() { 12 | printf("memory detect \n"); 13 | int *p1 = new int; 14 | delete p1; 15 | 16 | int *p2 = new int[4]; 17 | delete[] p2; 18 | 19 | A *a1 = new A; 20 | // delete a1; 21 | 22 | A *a2 = new A[1]; 23 | delete[] a2; 24 | 25 | { std::shared_ptr a = std::make_shared(); } 26 | checkLeaks(); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /thread/include/thread/count_down_latch.h: -------------------------------------------------------------------------------- 1 | #ifndef __COUNT_DOWN_LATCH__ 2 | #define __COUNT_DOWN_LATCH__ 3 | 4 | #include "common/noncopyable.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace wzq { 11 | class CountDownLatch : NonCopyAble { 12 | public: 13 | explicit CountDownLatch(uint32_t count); 14 | 15 | void CountDown(); 16 | 17 | void Await(uint32_t time_ms = 0); 18 | 19 | uint32_t GetCount() const; 20 | 21 | private: 22 | std::condition_variable cv_; 23 | mutable std::mutex mutex_; 24 | uint32_t count_ = 0; 25 | }; 26 | } // namespace wzq 27 | 28 | #endif -------------------------------------------------------------------------------- /singleton/include/singleton/singleton.h: -------------------------------------------------------------------------------- 1 | #ifndef __SINGLETON__ 2 | #define __SINGLETON__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace wzq { 9 | 10 | template 11 | class SingleTon { 12 | public: 13 | static T& instance() { 14 | std::call_once(once_, &SingleTon::init); 15 | return *value_; 16 | } 17 | 18 | private: 19 | SingleTon(); 20 | ~SingleTon(); 21 | 22 | SingleTon(const SingleTon&) = delete; 23 | SingleTon& operator=(const SingleTon&) = delete; 24 | 25 | static void init() { value_ = new T(); } 26 | static T* value_; 27 | 28 | static std::once_flag once_; 29 | }; 30 | 31 | } // namespace wzq 32 | 33 | #endif -------------------------------------------------------------------------------- /thread/src/count_down_latch.cc: -------------------------------------------------------------------------------- 1 | #include "thread/count_down_latch.h" 2 | #include 3 | #include 4 | 5 | namespace wzq { 6 | 7 | CountDownLatch::CountDownLatch(uint32_t count) : count_(count) {} 8 | 9 | void CountDownLatch::CountDown() { 10 | std::unique_lock lock(mutex_); 11 | --count_; 12 | if (count_ == 0) { 13 | cv_.notify_all(); 14 | } 15 | } 16 | 17 | void CountDownLatch::Await(uint32_t time_ms) { 18 | std::unique_lock lock(mutex_); 19 | while (count_ > 0) { 20 | if (time_ms > 0) { 21 | cv_.wait_for(lock, std::chrono::milliseconds(time_ms)); 22 | } else { 23 | cv_.wait(lock); 24 | } 25 | } 26 | } 27 | 28 | uint32_t CountDownLatch::GetCount() const{ 29 | std::unique_lock lock(mutex_); 30 | return count_; 31 | } 32 | 33 | } // namespace wzq -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0) 2 | project(wzq_utils) 3 | 4 | message( STATUS "CMake version ${CMAKE_VERSION}" ) 5 | message (STATUS "source dir: ${CMAKE_SOURCE_DIR}") 6 | message (STATUS "build type: ${CMAKE_BUILD_TYPE}") 7 | message (STATUS "compiler: ${CMAKE_CXX_COMPILER}") 8 | 9 | set (CMAKE_CXX_FLAGS "--std=c++17") 10 | 11 | include_directories(${CMAKE_SOURCE_DIR} 12 | ${CMAKE_SOURCE_DIR}/thread/include/ 13 | ${CMAKE_SOURCE_DIR}/timer/include/ 14 | ${CMAKE_SOURCE_DIR}/singleton/include/ 15 | ${CMAKE_SOURCE_DIR}/common/include/) 16 | 17 | add_subdirectory(thread) 18 | add_subdirectory(singleton) 19 | 20 | add_executable(test_wzq test.cc) 21 | target_link_libraries(test_wzq pthread) 22 | 23 | # target_link_libraries(test_thread wzq_thread) -------------------------------------------------------------------------------- /common/include/common/defer.h: -------------------------------------------------------------------------------- 1 | #ifndef __DEFER__ 2 | #define __DEFER__ 3 | 4 | #include "common/noncopyable.h" 5 | 6 | #include 7 | #include 8 | 9 | // reference https://github.com/loveyacper/ananas 10 | 11 | namespace wzq { 12 | 13 | class ExecuteOnScopeExit : wzq::NonCopyAble { 14 | public: 15 | ExecuteOnScopeExit() = default; 16 | 17 | ExecuteOnScopeExit(ExecuteOnScopeExit&&) = default; 18 | ExecuteOnScopeExit& operator=(ExecuteOnScopeExit&&) = default; 19 | 20 | template 21 | ExecuteOnScopeExit(F&& f, Args&&... args) { 22 | func_ = std::bind(std::forward(f), std::forward(args)...); 23 | } 24 | 25 | ~ExecuteOnScopeExit() noexcept { 26 | if (func_) { 27 | func_(); 28 | } 29 | } 30 | 31 | private: 32 | std::function func_; 33 | }; 34 | 35 | } // namespace wzq 36 | 37 | #define _CONCAT(a, b) a##b 38 | #define _MAKE_DEFER_(line) wzq::ExecuteOnScopeExit _CONCAT(defer, line) = [&]() 39 | 40 | #undef WZQ_DEFER 41 | #define WZQ_DEFER _MAKE_DEFER_(__LINE__) 42 | 43 | #endif -------------------------------------------------------------------------------- /common/include/common/own_strings.h: -------------------------------------------------------------------------------- 1 | #ifndef __OWNSTRINGS__ 2 | #define __OWNSTRINGS__ 3 | 4 | #include "common/noncopyable.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace wzq { 12 | 13 | class OwnedStrings : wzq::NonCopyAble { 14 | public: 15 | OwnedStrings(const std::vector &src) { 16 | char_ptr_vec.reserve(src.size() + 1); 17 | std::transform(src.begin(), src.end(), char_ptr_vec.begin(), [](const std::string &str) { 18 | char *buffer = new char[str.size() + 1]; 19 | std::copy(str.begin(), str.end(), buffer); 20 | buffer[str.size()] = 0; 21 | return buffer; 22 | }); 23 | char_ptr_vec.push_back(nullptr); 24 | } 25 | 26 | char **data() { return char_ptr_vec.data(); } 27 | 28 | ~OwnedStrings() { 29 | for (char *elem : char_ptr_vec) { 30 | if (elem != nullptr) { 31 | delete[] elem; 32 | } 33 | } 34 | } 35 | 36 | private: 37 | std::vector char_ptr_vec; 38 | }; 39 | 40 | } // namespace wzq 41 | 42 | #endif -------------------------------------------------------------------------------- /common/include/common/cmd.h: -------------------------------------------------------------------------------- 1 | #ifndef __CMD__ 2 | #define __CMD__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace wzq { 10 | class Command { 11 | public: 12 | static std::string RunCmd(const std::string &cmd, int32_t result_max_size = 10240) { 13 | char *data = new char[result_max_size]; 14 | if (data == nullptr) { 15 | return std::string(""); 16 | } 17 | bzero(data, sizeof(data)); 18 | const int max_buffer = 256; 19 | char buffer[max_buffer]; 20 | FILE *fdp = popen(cmd.c_str(), "r"); 21 | int data_len = 0; 22 | if (fdp) { 23 | while (!feof(fdp)) { 24 | if (fgets(buffer, max_buffer, fdp)) { 25 | int len = strlen(buffer); 26 | if ((data_len + len) > result_max_size) { 27 | break; 28 | } 29 | memcpy(data + data_len, buffer, len); 30 | data_len += len; 31 | } 32 | } 33 | pclose(fdp); 34 | } 35 | std::string ret = std::string(data, data_len); 36 | delete[] data; 37 | return ret; 38 | } 39 | }; 40 | } // namespace wzq 41 | 42 | #endif -------------------------------------------------------------------------------- /common/include/common/map.h: -------------------------------------------------------------------------------- 1 | #ifndef __THREAD_SAFE_MAP__ 2 | #define __THREAD_SAFE_MAP__ 3 | 4 | #include 5 | #include 6 | 7 | namespace wzq { 8 | // thread safe map 9 | template 10 | class ThreadSafeMap { 11 | public: 12 | void Emplace(const K& key, const V& v) { 13 | std::unique_lock lock(mutex_); 14 | map_[key] = v; 15 | } 16 | 17 | void Emplace(const K& key, V&& v) { 18 | std::unique_lock lock(mutex_); 19 | map_[key] = std::move(v); 20 | } 21 | 22 | void EraseKey(const K& key) { 23 | std::unique_lock lock(mutex_); 24 | if (map_.find(key) != map_.end()) { 25 | map_.erase(key); 26 | } 27 | } 28 | 29 | bool GetValueFromKey(const K& key, V& value) { 30 | std::unique_lock l(mutex_); 31 | if (map_.find(key) != map_.end()) { 32 | value = map_[key]; 33 | return true; 34 | } 35 | return false; 36 | } 37 | 38 | bool IsKeyExist(const K& key) { 39 | std::unique_lock l(mutex_); 40 | return map_.find(key) != map_.end(); 41 | } 42 | 43 | std::size_t Size() { 44 | std::unique_lock l(mutex_); 45 | return map_.size(); 46 | } 47 | 48 | private: 49 | std::map map_; 50 | std::mutex mutex_; 51 | }; 52 | 53 | } // namespace wzq 54 | 55 | #endif -------------------------------------------------------------------------------- /thread/test/test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "thread/count_down_latch.h" 5 | #include "thread/thread_pool.h" 6 | 7 | void TestThreadPool() { 8 | cout << "hello" << endl; 9 | wzq::ThreadPool pool(wzq::ThreadPool::ThreadPoolConfig{4, 5, 6, std::chrono::seconds(4)}); 10 | pool.Start(); 11 | std::this_thread::sleep_for(std::chrono::seconds(4)); 12 | cout << "thread size " << pool.GetTotalThreadSize() << endl; 13 | std::atomic index; 14 | index.store(0); 15 | std::thread t([&]() { 16 | for (int i = 0; i < 10; ++i) { 17 | pool.Run([&]() { 18 | cout << "function " << index.load() << endl; 19 | std::this_thread::sleep_for(std::chrono::seconds(4)); 20 | index++; 21 | }); 22 | // std::this_thread::sleep_for(std::chrono::seconds(2)); 23 | } 24 | }); 25 | t.detach(); 26 | cout << "=================" << endl; 27 | 28 | std::this_thread::sleep_for(std::chrono::seconds(4)); 29 | pool.Reset(wzq::ThreadPool::ThreadPoolConfig{4, 4, 6, std::chrono::seconds(4)}); 30 | std::this_thread::sleep_for(std::chrono::seconds(4)); 31 | cout << "thread size " << pool.GetTotalThreadSize() << endl; 32 | cout << "waiting size " << pool.GetWaitingThreadSize() << endl; 33 | cout << "---------------" << endl; 34 | // pool.ShutDownNow(); 35 | getchar(); 36 | cout << "world" << endl; 37 | } 38 | 39 | int main() { 40 | TestThreadPool(); 41 | return 0; 42 | std::cout << "hello world " << std::endl; 43 | wzq::CountDownLatch t(5); 44 | std::thread thread([&] { 45 | t.Await(0); 46 | std::cout << "await over" << std::endl; 47 | }); 48 | for (int i = 0; i < 5; i++) { 49 | t.CountDown(); 50 | std::cout << "count " << t.GetCount() << std::endl; 51 | } 52 | getchar(); 53 | return 0; 54 | } -------------------------------------------------------------------------------- /test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common/cmd.h" 5 | #include "common/defer.h" 6 | #include "common/noncopyable.h" 7 | #include "common/own_strings.h" 8 | #include "timer/timer.h" 9 | 10 | // 默认是private继承,禁止运行时多态即 wzq::NonCopyAble x = new Test(); 会出现编译错误 11 | class Test : wzq::NonCopyAble { 12 | public: 13 | Test() { std::cout << "Test constructor" << std::endl; } 14 | 15 | void Print() { std::cout << "Test " << std::endl; } 16 | 17 | ~Test() { std::cout << "Test deconstructor " << std::endl; } 18 | }; 19 | 20 | template 21 | std::ostream& operator<<(std::ostream& os, const std::vector& v) { 22 | if (v.empty()) { 23 | return os; 24 | } 25 | os << v.front(); 26 | 27 | for (std::size_t i = 1; i < v.size(); ++i) { 28 | os << ' ' << v[i]; 29 | } 30 | return os; 31 | } 32 | 33 | void TestTimerQueue() { 34 | wzq::TimerQueue q; 35 | q.Run(); 36 | for (int i = 5; i < 15; ++i) { 37 | q.AddFuncAfterDuration(std::chrono::seconds(i + 1), [i]() { std::cout << "this is " << i << std::endl; }); 38 | 39 | q.AddFuncAtTimePoint(std::chrono::high_resolution_clock::now() + std::chrono::seconds(1), 40 | [i]() { std::cout << "this is " << i << " at " << std::endl; }); 41 | } 42 | 43 | int id = q.AddRepeatedFunc(10, std::chrono::seconds(1), []() { std::cout << "func " << std::endl; }); 44 | std::this_thread::sleep_for(std::chrono::seconds(4)); 45 | q.CancelRepeatedFuncId(id); 46 | 47 | std::this_thread::sleep_for(std::chrono::seconds(30)); 48 | q.Stop(); 49 | } 50 | 51 | int main() { 52 | TestTimerQueue(); 53 | return 0; 54 | Test a; 55 | WZQ_DEFER { a.Print(); }; 56 | std::cout << "2" << std::endl; 57 | 58 | std::cout << wzq::Command::RunCmd("ls"); 59 | 60 | std::vector words = {"What", "a", "beautiful", "world"}; 61 | std::cout << words << '\n'; 62 | 63 | return 0; 64 | } -------------------------------------------------------------------------------- /experiment/test_type_traits.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // checking if Foo is constructible from double will cause a hard error 6 | struct Foo { 7 | template 8 | struct sfinae_unfriendly_check { 9 | static_assert(!std::is_same_v); 10 | }; 11 | 12 | template 13 | Foo(T, sfinae_unfriendly_check = {}); 14 | }; 15 | 16 | template 17 | struct first_constructible { 18 | template 19 | struct is_constructible_x : std::is_constructible { 20 | using type = T; 21 | }; 22 | struct fallback { 23 | static constexpr bool value = true; 24 | using type = void; // type to return if nothing is found 25 | }; 26 | 27 | template 28 | using with = typename std::disjunction..., fallback>::type; 29 | }; 30 | 31 | // OK, is_constructible not instantiated 32 | static_assert(std::is_same_v::with, int>); 33 | 34 | static_assert(std::is_same_v::with<>, std::string>); 35 | static_assert(std::is_same_v::with, std::string>); 36 | static_assert(std::is_same_v::with, void>); 37 | 38 | // func is enabled if all Ts... have the same type as T 39 | template 40 | std::enable_if_t...>> func(T, Ts...) { 41 | std::cout << "all types in pack are T\n"; 42 | } 43 | 44 | // otherwise 45 | template 46 | std::enable_if_t...>> func(T, Ts...) { 47 | std::cout << "not all types in pack are T\n"; 48 | } 49 | 50 | static_assert(std::is_same, typename std::negation>::type>::value, 51 | ""); 52 | static_assert(std::is_same, typename std::negation>::type>::value, 53 | ""); 54 | 55 | 56 | int main() { 57 | func(1, 2, 3); 58 | func(1, 2, "hello!"); 59 | std::cout << std::boolalpha; 60 | std::cout << std::negation>::value << '\n'; 61 | std::cout << std::negation>::value << '\n'; 62 | return 0; 63 | } -------------------------------------------------------------------------------- /experiment/type_rich_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct Unit { 5 | enum { metre = M, kilogram = K, second = S }; 6 | }; 7 | 8 | 9 | template 10 | class Value { 11 | private: 12 | long double magnitude_{0.0}; 13 | 14 | public: 15 | constexpr explicit Value(const long double magnitude) : magnitude_(magnitude) {} 16 | 17 | constexpr long double GetMagnitude() const noexcept { return magnitude_; } 18 | }; 19 | 20 | using Length = Value>; 21 | using Distance = Value>; 22 | using SpeedMs = Value>; 23 | using SpeedKmh = Value>; 24 | using Time = Value>; 25 | 26 | // 通过使用这种方式可以降低沟通成本,一个speed,如果是int,单位可能是m/s,也有可能是km/h, 27 | // 需要沟通传入的究竟是什么单位,如果沟通有误就会引起严重后果, 28 | // 都统一使用这种方式就肯定会保证使用的是同一个单位,只能传入SpeedMs或SpeedKmh 29 | void UseSpeed(const SpeedMs& speed) { std::cout << "speed " << speed.GetMagnitude() << std::endl; } 30 | 31 | template 32 | constexpr Value> operator+(const Value>& l, const Value>& r) noexcept { 33 | return Value>(l.GetMagnitude() + r.GetMagnitude()); 34 | } 35 | 36 | // 这里的M,K,S的取值是个学问,可以自己根据实际情况计算好,distance=time*speed 37 | // 每个都是不同的M,K,S,当我们定义他们的加减乘除操作符的时候就需要考虑好, 38 | // 如果能够保证time和speed的M,K,S运算后是distance的类型呢 39 | template 40 | constexpr Value> operator*(const Value>& l, 41 | const Value>& r) noexcept { 42 | return Value>(l.GetMagnitude() * r.GetMagnitude()); 43 | } 44 | 45 | void TestValue() { 46 | SpeedMs ms{1.2}; 47 | UseSpeed(ms); 48 | SpeedMs mss = SpeedMs{1.4} + SpeedMs{1.5}; 49 | UseSpeed(mss); 50 | } 51 | 52 | // 为了更加直观的看也可以为文字添加自定义后缀,所谓的文字操作符 53 | constexpr SpeedMs operator"" _ms(long double magnitude) { 54 | return SpeedMs(magnitude); 55 | } 56 | 57 | constexpr SpeedKmh operator"" _kmh(long double magnitude) { 58 | return SpeedKmh(magnitude); 59 | } 60 | 61 | constexpr Length operator"" _m(long double magnitude) { 62 | return Length(magnitude); 63 | } 64 | 65 | void TestPostfix() { 66 | constexpr SpeedMs speed_ms = 1.2_ms; 67 | constexpr SpeedKmh speed_kmh = 1.3_kmh; 68 | constexpr Length length = 1.4_m; 69 | std::cout << speed_ms.GetMagnitude() << std::endl; 70 | std::cout << speed_kmh.GetMagnitude() << std::endl; 71 | std::cout << length.GetMagnitude() << std::endl; 72 | 73 | long l; 74 | double d; 75 | long double ld; 76 | std::cout << sizeof(l) << std::endl; 77 | std::cout << sizeof(d) << std::endl; 78 | std::cout << sizeof(ld) << std::endl; 79 | } 80 | 81 | int main() { 82 | TestValue(); 83 | TestPostfix(); 84 | return 0; 85 | } -------------------------------------------------------------------------------- /experiment/output_container.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::ostream 4 | #include // std::false_type/true_type/decay_t/is_same_v 5 | #include // std::declval/pair 6 | 7 | // Type trait to detect std::pair 8 | template 9 | struct is_pair : std::false_type {}; 10 | template 11 | struct is_pair> : std::true_type {}; 12 | template 13 | inline constexpr bool is_pair_v = is_pair::value; 14 | 15 | // Type trait to detect whether an output function already exists 16 | template 17 | struct has_output_function { 18 | template 19 | static auto output(U* ptr) -> decltype(std::declval() << *ptr, std::true_type()); 20 | template 21 | static std::false_type output(...); 22 | static constexpr bool value = decltype(output(nullptr))::value; 23 | }; 24 | template 25 | inline constexpr bool has_output_function_v = has_output_function::value; 26 | /* NB: Visual Studio 2017 (or below) may have problems with 27 | * has_output_function_v: you should then use 28 | * has_output_function::value instead, or upgrade to 29 | * Visual Studio 2019. */ 30 | 31 | // Output function for std::pair 32 | template 33 | std::ostream& operator<<(std::ostream& os, const std::pair& pr); 34 | 35 | // Element output function for containers that define a key_type and 36 | // have its value type as std::pair 37 | template 38 | auto output_element(std::ostream& os, const T& element, const Cont&, const std::true_type) 39 | -> decltype(std::declval(), os); 40 | // Element output function for other containers 41 | template 42 | auto output_element(std::ostream& os, const T& element, const Cont&, ...) -> decltype(os); 43 | 44 | // Main output function, enabled only if no output function already exists 45 | template >> 46 | auto operator<<(std::ostream& os, const T& container) -> decltype(container.begin(), container.end(), os) { 47 | using std::decay_t; 48 | using std::is_same_v; 49 | 50 | using element_type = decay_t; 51 | constexpr bool is_char_v = is_same_v; 52 | if constexpr (!is_char_v) { 53 | os << '{'; 54 | } 55 | auto end = container.end(); 56 | bool on_first_element = true; 57 | for (auto it = container.begin(); it != end; ++it) { 58 | if constexpr (is_char_v) { 59 | if (*it == '\0') { 60 | break; 61 | } 62 | } else { 63 | if (!on_first_element) { 64 | os << ", "; 65 | } else { 66 | os << ' '; 67 | on_first_element = false; 68 | } 69 | } 70 | output_element(os, *it, container, is_pair()); 71 | } 72 | if constexpr (!is_char_v) { 73 | if (!on_first_element) { // Not empty 74 | os << ' '; 75 | } 76 | os << '}'; 77 | } 78 | return os; 79 | } 80 | 81 | template 82 | auto output_element(std::ostream& os, const T& element, const Cont&, const std::true_type) 83 | -> decltype(std::declval(), os) { 84 | os << element.first << " => " << element.second; 85 | return os; 86 | } 87 | 88 | template 89 | auto output_element(std::ostream& os, const T& element, const Cont&, ...) -> decltype(os) { 90 | os << element; 91 | return os; 92 | } 93 | 94 | template 95 | std::ostream& operator<<(std::ostream& os, const std::pair& pr) { 96 | os << '(' << pr.first << ", " << pr.second << ')'; 97 | return os; 98 | } 99 | -------------------------------------------------------------------------------- /timer/include/timer/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef __TIMER__ 2 | #define __TIMER__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "common/map.h" 14 | #include "thread/thread_pool.h" 15 | 16 | namespace wzq { 17 | class TimerQueue { 18 | public: 19 | struct InternalS { 20 | std::chrono::time_point time_point_; 21 | std::function func_; 22 | int repeated_id; 23 | bool operator<(const InternalS& b) const { return time_point_ > b.time_point_; } 24 | }; 25 | 26 | public: 27 | bool Run() { 28 | bool ret = thread_pool_.Start(); 29 | if (!ret) { 30 | return false; 31 | } 32 | std::thread([this]() { RunLocal(); }).detach(); 33 | return true; 34 | } 35 | 36 | bool IsAvailable() { return thread_pool_.IsAvailable(); } 37 | 38 | int Size() { return queue_.size(); } 39 | 40 | void Stop() { 41 | running_.store(false); 42 | cond_.notify_all(); 43 | thread_pool_.ShutDown(); 44 | } 45 | 46 | template 47 | void AddFuncAfterDuration(const std::chrono::duration& time, F&& f, Args&&... args) { 48 | InternalS s; 49 | s.time_point_ = std::chrono::high_resolution_clock::now() + time; 50 | s.func_ = std::bind(std::forward(f), std::forward(args)...); 51 | std::unique_lock lock(mutex_); 52 | queue_.push(s); 53 | cond_.notify_all(); 54 | } 55 | 56 | template 57 | void AddFuncAtTimePoint(const std::chrono::time_point& time_point, F&& f, 58 | Args&&... args) { 59 | InternalS s; 60 | s.time_point_ = time_point; 61 | s.func_ = std::bind(std::forward(f), std::forward(args)...); 62 | std::unique_lock lock(mutex_); 63 | queue_.push(s); 64 | cond_.notify_all(); 65 | } 66 | 67 | template 68 | int AddRepeatedFunc(int repeat_num, const std::chrono::duration& time, F&& f, Args&&... args) { 69 | int id = GetNextRepeatedFuncId(); 70 | repeated_id_state_map_.Emplace(id, RepeatedIdState::kRunning); 71 | auto tem_func = std::bind(std::forward(f), std::forward(args)...); 72 | AddRepeatedFuncLocal(repeat_num - 1, time, id, std::move(tem_func)); 73 | return id; 74 | } 75 | 76 | void CancelRepeatedFuncId(int func_id) { repeated_id_state_map_.EraseKey(func_id); } 77 | 78 | int GetNextRepeatedFuncId() { return repeated_func_id_++; } 79 | 80 | TimerQueue() : thread_pool_(wzq::ThreadPool::ThreadPoolConfig{4, 4, 40, std::chrono::seconds(4)}) { 81 | repeated_func_id_.store(0); 82 | running_.store(true); 83 | } 84 | 85 | ~TimerQueue() { Stop(); } 86 | 87 | enum class RepeatedIdState { kInit = 0, kRunning = 1, kStop = 2 }; 88 | 89 | private: 90 | void RunLocal() { 91 | while (running_.load()) { 92 | std::unique_lock lock(mutex_); 93 | if (queue_.empty()) { 94 | cond_.wait(lock); 95 | continue; 96 | } 97 | auto s = queue_.top(); 98 | auto diff = s.time_point_ - std::chrono::high_resolution_clock::now(); 99 | if (std::chrono::duration_cast(diff).count() > 0) { 100 | cond_.wait_for(lock, diff); 101 | continue; 102 | } else { 103 | queue_.pop(); 104 | lock.unlock(); 105 | thread_pool_.Run(std::move(s.func_)); 106 | } 107 | } 108 | } 109 | 110 | template 111 | void AddRepeatedFuncLocal(int repeat_num, const std::chrono::duration& time, int id, F&& f) { 112 | if (!this->repeated_id_state_map_.IsKeyExist(id)) { 113 | return; 114 | } 115 | InternalS s; 116 | s.time_point_ = std::chrono::high_resolution_clock::now() + time; 117 | auto tem_func = std::move(f); 118 | s.repeated_id = id; 119 | s.func_ = [this, &tem_func, repeat_num, time, id]() { 120 | tem_func(); 121 | if (!this->repeated_id_state_map_.IsKeyExist(id) || repeat_num == 0) { 122 | return; 123 | } 124 | AddRepeatedFuncLocal(repeat_num - 1, time, id, std::move(tem_func)); 125 | }; 126 | std::unique_lock lock(mutex_); 127 | queue_.push(s); 128 | lock.unlock(); 129 | cond_.notify_all(); 130 | } 131 | 132 | private: 133 | std::priority_queue queue_; 134 | std::atomic running_; 135 | std::mutex mutex_; 136 | std::condition_variable cond_; 137 | 138 | wzq::ThreadPool thread_pool_; 139 | 140 | std::atomic repeated_func_id_; 141 | wzq::ThreadSafeMap repeated_id_state_map_; 142 | }; 143 | 144 | } // namespace wzq 145 | 146 | #endif -------------------------------------------------------------------------------- /memory/MemoryDetect.cc: -------------------------------------------------------------------------------- 1 | #include "MemoryDetect.h" 2 | 3 | #ifdef new 4 | #undef new 5 | #endif 6 | 7 | #include // assert 8 | #include // abort 9 | #include // strcpy/strncpy/sprintf 10 | 11 | #include 12 | #include 13 | 14 | int checkLeaks(); 15 | int checkMemCorruption(); 16 | 17 | #ifndef _DEBUG_NEW_ALIGNMENT 18 | #define _DEBUG_NEW_ALIGNMENT 16 19 | #endif 20 | 21 | #ifndef _DEBUG_NEW_CALLER_ADDRESS 22 | #ifdef __GNUC__ 23 | #define _DEBUG_NEW_CALLER_ADDRESS __builtin_return_address(0) 24 | #else 25 | #define _DEBUG_NEW_CALLER_ADDRESS nullptr 26 | #endif 27 | #endif 28 | 29 | #ifndef _DEBUG_NEW_FILENAME_LEN 30 | #define _DEBUG_NEW_FILENAME_LEN 200 31 | #endif 32 | 33 | #define ALIGN(s) (((s) + _DEBUG_NEW_ALIGNMENT - 1) & ~(_DEBUG_NEW_ALIGNMENT - 1)) 34 | 35 | struct new_ptr_list_t { 36 | new_ptr_list_t* next; ///< Pointer to the next memory block 37 | new_ptr_list_t* prev; ///< Pointer to the previous memory block 38 | std::size_t size; ///< Size of the memory block 39 | union { 40 | char file[_DEBUG_NEW_FILENAME_LEN]; ///< File name of the caller 41 | 42 | void* addr; ///< Address of the caller to \e new 43 | }; 44 | unsigned line : 31; ///< Line number of the caller; or \c 0 45 | unsigned is_array : 1; ///< Non-zero iff new[] is used 46 | unsigned magic; ///< Magic number for error detection 47 | }; 48 | 49 | static const unsigned DEBUG_NEW_MAGIC = 0x4442474E; 50 | 51 | static const int ALIGNED_LIST_ITEM_SIZE = ALIGN(sizeof(new_ptr_list_t)); 52 | 53 | static new_ptr_list_t new_ptr_list = {&new_ptr_list, &new_ptr_list, 0, {""}, 0, 0, DEBUG_NEW_MAGIC}; 54 | 55 | static std::mutex new_ptr_lock; 56 | 57 | static std::mutex new_output_lock; 58 | 59 | static std::size_t total_mem_alloc = 0; 60 | 61 | bool new_autocheck_flag = true; 62 | 63 | bool new_verbose_flag = false; 64 | 65 | static void print_position(const void* ptr, int line) { 66 | if (line != 0) { // Is file/line information present? 67 | printf("%s:%d", (const char*)ptr, line); 68 | } else if (ptr != nullptr) { // Is caller address present? 69 | printf("%p", ptr); 70 | } else { // No information is present 71 | printf(""); 72 | } 73 | } 74 | 75 | static void* alloc_mem(std::size_t size, const char* file, int line, bool is_array) { 76 | assert(line >= 0); 77 | 78 | std::size_t s = size + ALIGNED_LIST_ITEM_SIZE; 79 | new_ptr_list_t* ptr = (new_ptr_list_t*)malloc(s); 80 | if (ptr == nullptr) { 81 | std::unique_lock lock(new_output_lock); 82 | printf("Out of memory when allocating %lu bytes\n", (unsigned long)size); 83 | abort(); 84 | } 85 | void* usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; 86 | 87 | if (line) { 88 | strncpy(ptr->file, file, _DEBUG_NEW_FILENAME_LEN - 1)[_DEBUG_NEW_FILENAME_LEN - 1] = '\0'; 89 | } else { 90 | ptr->addr = (void*)file; 91 | } 92 | 93 | ptr->line = line; 94 | ptr->is_array = is_array; 95 | ptr->size = size; 96 | ptr->magic = DEBUG_NEW_MAGIC; 97 | { 98 | std::unique_lock lock(new_ptr_lock); 99 | ptr->prev = new_ptr_list.prev; 100 | ptr->next = &new_ptr_list; 101 | new_ptr_list.prev->next = ptr; 102 | new_ptr_list.prev = ptr; 103 | } 104 | if (new_verbose_flag) { 105 | std::unique_lock lock(new_output_lock); 106 | printf("new%s: allocated %p (size %lu, ", is_array ? "[]" : "", usr_ptr, (unsigned long)size); 107 | if (line != 0) { 108 | print_position(ptr->file, ptr->line); 109 | } else { 110 | print_position(ptr->addr, ptr->line); 111 | } 112 | printf(")\n"); 113 | } 114 | total_mem_alloc += size; 115 | return usr_ptr; 116 | } 117 | 118 | static void free_pointer(void* usr_ptr, void* addr, bool is_array) { 119 | if (usr_ptr == nullptr) { 120 | return; 121 | } 122 | new_ptr_list_t* ptr = (new_ptr_list_t*)((char*)usr_ptr - ALIGNED_LIST_ITEM_SIZE); 123 | if (ptr->magic != DEBUG_NEW_MAGIC) { // 可以检测栈是否有损坏 124 | { 125 | std::unique_lock lock(new_output_lock); 126 | printf("delete%s: invalid pointer %p (", is_array ? "[]" : "", usr_ptr); 127 | print_position(addr, 0); 128 | printf(")\n"); 129 | } 130 | checkMemCorruption(); 131 | abort(); 132 | } 133 | if ((unsigned)is_array != ptr->is_array) { // 可以检测new delete new[] delete[]是否配对使用 134 | const char* msg; 135 | if (is_array) { 136 | msg = "delete[] after new"; 137 | } else { 138 | msg = "delete after new[]"; 139 | } 140 | std::unique_lock lock(new_output_lock); 141 | printf("%s: pointer %p (size %lu)\n\tat ", msg, (char*)ptr + ALIGNED_LIST_ITEM_SIZE, (unsigned long)ptr->size); 142 | print_position(addr, 0); 143 | printf("\n\toriginally allocated at "); 144 | if (ptr->line != 0) { 145 | print_position(ptr->file, ptr->line); 146 | } else { 147 | print_position(ptr->addr, ptr->line); 148 | } 149 | printf("\n"); 150 | abort(); 151 | } 152 | 153 | { 154 | std::unique_lock lock(new_ptr_lock); 155 | total_mem_alloc -= ptr->size; 156 | ptr->magic = 0; 157 | ptr->prev->next = ptr->next; 158 | ptr->next->prev = ptr->prev; 159 | } 160 | 161 | if (new_verbose_flag) { 162 | std::unique_lock lock(new_output_lock); 163 | printf("delete%s: freed %p (size %lu, %lu bytes still allocated)\n", is_array ? "[]" : "", 164 | (char*)ptr + ALIGNED_LIST_ITEM_SIZE, (unsigned long)ptr->size, (unsigned long)total_mem_alloc); 165 | } 166 | free(ptr); 167 | } 168 | 169 | int checkLeaks() { 170 | int leak_cnt = 0; 171 | int whitelisted_leak_cnt = 0; 172 | new_ptr_list_t* ptr = new_ptr_list.next; 173 | 174 | while (ptr != &new_ptr_list) { 175 | const char* const usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; 176 | if (ptr->magic != DEBUG_NEW_MAGIC) { 177 | printf("warning: heap data corrupt near %p\n", usr_ptr); 178 | } 179 | 180 | printf("Leaked object at %p (size %lu, ", usr_ptr, (unsigned long)ptr->size); 181 | 182 | if (ptr->line != 0) { 183 | print_position(ptr->file, ptr->line); 184 | } else { 185 | print_position(ptr->addr, ptr->line); 186 | } 187 | 188 | printf(")\n"); 189 | 190 | ptr = ptr->next; 191 | ++leak_cnt; 192 | } 193 | if (new_verbose_flag || leak_cnt) { 194 | printf("*** %d leaks found\n", leak_cnt); 195 | } 196 | 197 | return leak_cnt; 198 | } 199 | 200 | int checkMemCorruption() { 201 | int corrupt_cnt = 0; 202 | printf("*** Checking for memory corruption: START\n"); 203 | for (new_ptr_list_t* ptr = new_ptr_list.next; ptr != &new_ptr_list; ptr = ptr->next) { 204 | const char* const usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; 205 | if (ptr->magic == DEBUG_NEW_MAGIC 206 | 207 | ) { 208 | continue; 209 | } 210 | 211 | printf("Heap data corrupt near %p (size %lu, ", usr_ptr, (unsigned long)ptr->size); 212 | 213 | if (ptr->line != 0) { 214 | print_position(ptr->file, ptr->line); 215 | } else { 216 | print_position(ptr->addr, ptr->line); 217 | } 218 | printf(")\n"); 219 | ++corrupt_cnt; 220 | } 221 | printf("*** Checking for memory corruption: %d FOUND\n", corrupt_cnt); 222 | return corrupt_cnt; 223 | } 224 | 225 | void* operator new(std::size_t size, const char* file, int line) { 226 | void* ptr = alloc_mem(size, file, line, false); 227 | return ptr; 228 | } 229 | 230 | void* operator new[](std::size_t size, const char* file, int line) { 231 | void* ptr = alloc_mem(size, file, line, true); 232 | return ptr; 233 | } 234 | 235 | void* operator new(std::size_t size) { return operator new(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0); } 236 | 237 | void* operator new[](std::size_t size) { return operator new[](size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0); } 238 | 239 | void operator delete(void* ptr) noexcept { free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, false); } 240 | 241 | void operator delete[](void* ptr) noexcept { free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, true); } -------------------------------------------------------------------------------- /thread/include/thread/thread_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef __THREAD_POOL__ 2 | #define __THREAD_POOL__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | using std::cout; 20 | using std::endl; 21 | 22 | namespace wzq { 23 | 24 | class ThreadPool { 25 | public: 26 | using PoolSeconds = std::chrono::seconds; 27 | 28 | /** 线程池的配置 29 | * core_threads: 核心线程个数,线程池中最少拥有的线程个数,初始化就会创建好的线程,常驻于线程池 30 | * 31 | * max_threads: >=core_threads,当任务的个数太多线程池执行不过来时, 32 | * 内部就会创建更多的线程用于执行更多的任务,内部线程数不会超过max_threads 33 | * 34 | * max_task_size: 内部允许存储的最大任务个数,暂时没有使用 35 | * 36 | * time_out: Cache线程的超时时间,Cache线程指的是max_threads-core_threads的线程, 37 | * 当time_out时间内没有执行任务,此线程就会被自动回收 38 | */ 39 | struct ThreadPoolConfig { 40 | int core_threads; 41 | int max_threads; 42 | int max_task_size; 43 | PoolSeconds time_out; 44 | }; 45 | 46 | /** 47 | * 线程的状态:有等待、运行、停止 48 | */ 49 | enum class ThreadState { kInit = 0, kWaiting = 1, kRunning = 2, kStop = 3 }; 50 | 51 | /** 52 | * 线程的种类标识:标志该线程是核心线程还是Cache线程,Cache是内部为了执行更多任务临时创建出来的 53 | */ 54 | enum class ThreadFlag { kInit = 0, kCore = 1, kCache = 2 }; 55 | 56 | using ThreadPtr = std::shared_ptr; 57 | using ThreadId = std::atomic; 58 | using ThreadStateAtomic = std::atomic; 59 | using ThreadFlagAtomic = std::atomic; 60 | 61 | /** 62 | * 线程池中线程存在的基本单位,每个线程都有个自定义的ID,有线程种类标识和状态 63 | */ 64 | struct ThreadWrapper { 65 | ThreadPtr ptr; 66 | ThreadId id; 67 | ThreadFlagAtomic flag; 68 | ThreadStateAtomic state; 69 | 70 | ThreadWrapper() { 71 | ptr = nullptr; 72 | id = 0; 73 | state.store(ThreadState::kInit); 74 | } 75 | }; 76 | using ThreadWrapperPtr = std::shared_ptr; 77 | using ThreadPoolLock = std::unique_lock; 78 | 79 | ThreadPool(ThreadPoolConfig config) : config_(config) { 80 | this->total_function_num_.store(0); 81 | this->waiting_thread_num_.store(0); 82 | 83 | this->thread_id_.store(0); 84 | this->is_shutdown_.store(false); 85 | this->is_shutdown_now_.store(false); 86 | 87 | if (IsValidConfig(config_)) { 88 | is_available_.store(true); 89 | } else { 90 | is_available_.store(false); 91 | } 92 | } 93 | 94 | ~ThreadPool() { ShutDown(); } 95 | 96 | bool Reset(ThreadPoolConfig config) { 97 | if (!IsValidConfig(config)) { 98 | return false; 99 | } 100 | if (config_.core_threads != config.core_threads) { 101 | return false; 102 | } 103 | config_ = config; 104 | return true; 105 | } 106 | 107 | // 开启线程池功能 108 | bool Start() { 109 | if (!IsAvailable()) { 110 | return false; 111 | } 112 | int core_thread_num = config_.core_threads; 113 | cout << "Init thread num " << core_thread_num << endl; 114 | while (core_thread_num-- > 0) { 115 | AddThread(GetNextThreadId()); 116 | } 117 | cout << "Init thread end" << endl; 118 | return true; 119 | } 120 | 121 | // 获取正在处于等待状态的线程的个数 122 | int GetWaitingThreadSize() { return this->waiting_thread_num_.load(); } 123 | 124 | // 获取线程池中当前线程的总个数 125 | int GetTotalThreadSize() { return this->worker_threads_.size(); } 126 | 127 | // 放在线程池中执行函数 128 | template 129 | auto Run(F &&f, Args &&... args) -> std::shared_ptr>> { 130 | if (this->is_shutdown_.load() || this->is_shutdown_now_.load() || !IsAvailable()) { 131 | return nullptr; 132 | } 133 | if (GetWaitingThreadSize() == 0 && GetTotalThreadSize() < config_.max_threads) { 134 | AddThread(GetNextThreadId(), ThreadFlag::kCache); 135 | } 136 | 137 | using return_type = std::result_of_t; 138 | auto task = std::make_shared>( 139 | std::bind(std::forward(f), std::forward(args)...)); 140 | total_function_num_++; 141 | 142 | std::future res = task->get_future(); 143 | { 144 | ThreadPoolLock lock(this->task_mutex_); 145 | this->tasks_.emplace([task]() { (*task)(); }); 146 | } 147 | this->task_cv_.notify_one(); 148 | return std::make_shared>>(std::move(res)); 149 | } 150 | 151 | // 获取当前线程池已经执行过的函数个数 152 | int GetRunnedFuncNum() { return total_function_num_.load(); } 153 | 154 | // 关掉线程池,内部还没有执行的任务会继续执行 155 | void ShutDown() { 156 | ShutDown(false); 157 | cout << "shutdown" << endl; 158 | } 159 | 160 | // 执行关掉线程池,内部还没有执行的任务直接取消,不会再执行 161 | void ShutDownNow() { 162 | ShutDown(true); 163 | cout << "shutdown now" << endl; 164 | } 165 | 166 | // 当前线程池是否可用 167 | bool IsAvailable() { return is_available_.load(); } 168 | 169 | private: 170 | void ShutDown(bool is_now) { 171 | if (is_available_.load()) { 172 | if (is_now) { 173 | this->is_shutdown_now_.store(true); 174 | } else { 175 | this->is_shutdown_.store(true); 176 | } 177 | this->task_cv_.notify_all(); 178 | is_available_.store(false); 179 | } 180 | } 181 | 182 | void AddThread(int id) { AddThread(id, ThreadFlag::kCore); } 183 | 184 | void AddThread(int id, ThreadFlag thread_flag) { 185 | cout << "AddThread " << id << " flag " << static_cast(thread_flag) << endl; 186 | ThreadWrapperPtr thread_ptr = std::make_shared(); 187 | thread_ptr->id.store(id); 188 | thread_ptr->flag.store(thread_flag); 189 | auto func = [this, thread_ptr]() { 190 | for (;;) { 191 | std::function task; 192 | { 193 | ThreadPoolLock lock(this->task_mutex_); 194 | if (thread_ptr->state.load() == ThreadState::kStop) { 195 | break; 196 | } 197 | cout << "thread id " << thread_ptr->id.load() << " running start" << endl; 198 | thread_ptr->state.store(ThreadState::kWaiting); 199 | ++this->waiting_thread_num_; 200 | bool is_timeout = false; 201 | if (thread_ptr->flag.load() == ThreadFlag::kCore) { 202 | this->task_cv_.wait(lock, [this, thread_ptr] { 203 | return (this->is_shutdown_ || this->is_shutdown_now_ || !this->tasks_.empty() || 204 | thread_ptr->state.load() == ThreadState::kStop); 205 | }); 206 | } else { 207 | this->task_cv_.wait_for(lock, this->config_.time_out, [this, thread_ptr] { 208 | return (this->is_shutdown_ || this->is_shutdown_now_ || !this->tasks_.empty() || 209 | thread_ptr->state.load() == ThreadState::kStop); 210 | }); 211 | is_timeout = !(this->is_shutdown_ || this->is_shutdown_now_ || !this->tasks_.empty() || 212 | thread_ptr->state.load() == ThreadState::kStop); 213 | } 214 | --this->waiting_thread_num_; 215 | cout << "thread id " << thread_ptr->id.load() << " running wait end" << endl; 216 | 217 | if (is_timeout) { 218 | thread_ptr->state.store(ThreadState::kStop); 219 | } 220 | 221 | if (thread_ptr->state.load() == ThreadState::kStop) { 222 | cout << "thread id " << thread_ptr->id.load() << " state stop" << endl; 223 | break; 224 | } 225 | if (this->is_shutdown_ && this->tasks_.empty()) { 226 | cout << "thread id " << thread_ptr->id.load() << " shutdown" << endl; 227 | break; 228 | } 229 | if (this->is_shutdown_now_) { 230 | cout << "thread id " << thread_ptr->id.load() << " shutdown now" << endl; 231 | break; 232 | } 233 | thread_ptr->state.store(ThreadState::kRunning); 234 | task = std::move(this->tasks_.front()); 235 | this->tasks_.pop(); 236 | } 237 | task(); 238 | } 239 | cout << "thread id " << thread_ptr->id.load() << " running end" << endl; 240 | }; 241 | thread_ptr->ptr = std::make_shared(std::move(func)); 242 | if (thread_ptr->ptr->joinable()) { 243 | thread_ptr->ptr->detach(); 244 | } 245 | this->worker_threads_.emplace_back(std::move(thread_ptr)); 246 | } 247 | 248 | void Resize(int thread_num) { 249 | if (thread_num < config_.core_threads) return; 250 | int old_thread_num = worker_threads_.size(); 251 | cout << "old num " << old_thread_num << " resize " << thread_num << endl; 252 | if (thread_num > old_thread_num) { 253 | while (thread_num-- > old_thread_num) { 254 | AddThread(GetNextThreadId()); 255 | } 256 | } else { 257 | int diff = old_thread_num - thread_num; 258 | auto iter = worker_threads_.begin(); 259 | while (iter != worker_threads_.end()) { 260 | if (diff == 0) { 261 | break; 262 | } 263 | auto thread_ptr = *iter; 264 | if (thread_ptr->flag.load() == ThreadFlag::kCache && 265 | thread_ptr->state.load() == ThreadState::kWaiting) { // wait 266 | thread_ptr->state.store(ThreadState::kStop); // stop; 267 | --diff; 268 | iter = worker_threads_.erase(iter); 269 | } else { 270 | ++iter; 271 | } 272 | } 273 | this->task_cv_.notify_all(); 274 | } 275 | } 276 | 277 | int GetNextThreadId() { return this->thread_id_++; } 278 | 279 | bool IsValidConfig(ThreadPoolConfig config) { 280 | if (config.core_threads < 1 || config.max_threads < config.core_threads || config.time_out.count() < 1) { 281 | return false; 282 | } 283 | return true; 284 | } 285 | 286 | private: 287 | ThreadPoolConfig config_; 288 | 289 | std::list worker_threads_; 290 | 291 | std::queue> tasks_; 292 | std::mutex task_mutex_; 293 | std::condition_variable task_cv_; 294 | 295 | std::atomic total_function_num_; 296 | std::atomic waiting_thread_num_; 297 | std::atomic thread_id_; 298 | 299 | std::atomic is_shutdown_now_; 300 | std::atomic is_shutdown_; 301 | std::atomic is_available_; 302 | }; 303 | 304 | } // namespace wzq 305 | 306 | #endif -------------------------------------------------------------------------------- /experiment/thread_pool.cc: -------------------------------------------------------------------------------- 1 | /* 2 | 这里参考java附上个人认为的一个稍微好点的线程池需要的功能(当前线程池都不支持...): 3 | 支持core_threads: 正常线程池应该有多少个线程 4 | 支持max_task_size: 最多允许的任务个数 5 | 支持max_threads: 可以允许的最大线程个数 6 | 支持time_out: 超时时间,多出来的线程空闲一定时间后回收掉 7 | 可获取当前线程池中空闲线程个数 8 | 可获取当前线程池中总线程个数 9 | 支持定时器功能和循环执行任务功能 10 | start()开启线程池功能 11 | shutdown()关闭,当前线程池里的任务需要执行完 12 | shutdown_now()立刻关闭,线程池里的任务全部清空 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | using std::cout; 31 | using std::endl; 32 | 33 | class ThreadPool { 34 | public: 35 | using PoolSeconds = std::chrono::seconds; 36 | struct ThreadPoolConfig { 37 | int core_threads; 38 | int max_threads; 39 | int max_task_size; 40 | PoolSeconds time_out; 41 | }; 42 | 43 | enum class ThreadState { kInit = 0, kWaiting = 1, kRunning = 2, kStop = 3 }; 44 | enum class ThreadFlag { kInit = 0, kCore = 1, kCache = 2 }; 45 | 46 | using ThreadPtr = std::shared_ptr; 47 | using ThreadId = std::atomic; 48 | using ThreadStateAtomic = std::atomic; 49 | using ThreadFlagAtomic = std::atomic; 50 | struct ThreadWrapper { 51 | ThreadPtr ptr; 52 | ThreadId id; 53 | ThreadFlagAtomic flag; 54 | ThreadStateAtomic state; 55 | 56 | ThreadWrapper() { 57 | ptr = nullptr; 58 | id = 0; 59 | state.store(ThreadState::kInit); 60 | } 61 | }; 62 | using ThreadWrapperPtr = std::shared_ptr; 63 | using ThreadPoolLock = std::unique_lock; 64 | 65 | ThreadPool(ThreadPoolConfig config) : config_(config) { 66 | this->total_function_num_.store(0); 67 | this->waiting_thread_num_.store(0); 68 | 69 | this->thread_id_.store(0); 70 | this->is_shutdown_.store(false); 71 | this->is_shutdown_now_.store(false); 72 | 73 | if (IsValidConfig(config_)) { 74 | is_available_.store(true); 75 | } else { 76 | is_available_.store(false); 77 | } 78 | } 79 | 80 | ~ThreadPool() { ShutDown(); } 81 | 82 | bool Reset(ThreadPoolConfig config) { 83 | if (!IsValidConfig(config)) { 84 | return false; 85 | } 86 | if (config_.core_threads != config.core_threads) { 87 | return false; 88 | } 89 | config_ = config; 90 | return true; 91 | } 92 | 93 | bool Start() { 94 | if (!IsAvailable()) { 95 | return false; 96 | } 97 | int core_thread_num = config_.core_threads; 98 | cout << "Init thread num " << core_thread_num << endl; 99 | while (core_thread_num-- > 0) { 100 | AddThread(GetNextThreadId()); 101 | } 102 | cout << "Init thread end" << endl; 103 | return true; 104 | } 105 | 106 | int GetWaitingThreadSize() { return this->waiting_thread_num_.load(); } 107 | 108 | int GetTotalThreadSize() { return this->worker_threads_.size(); } 109 | 110 | template 111 | auto Run(F &&f, Args &&... args) -> std::shared_ptr>> { 112 | if (this->is_shutdown_.load() || this->is_shutdown_now_.load() || !IsAvailable()) { 113 | return nullptr; 114 | } 115 | if (GetWaitingThreadSize() == 0 && GetTotalThreadSize() < config_.max_threads) { 116 | AddThread(GetNextThreadId(), ThreadFlag::kCache); 117 | } 118 | 119 | using return_type = std::result_of_t; 120 | auto task = std::make_shared>( 121 | std::bind(std::forward(f), std::forward(args)...)); 122 | total_function_num_++; 123 | 124 | std::future res = task->get_future(); 125 | { 126 | ThreadPoolLock lock(this->task_mutex_); 127 | this->tasks_.emplace([task]() { (*task)(); }); 128 | } 129 | this->task_cv_.notify_one(); 130 | return std::make_shared>>(std::move(res)); 131 | } 132 | 133 | int GetRunnedFuncNum() { return total_function_num_.load(); } 134 | 135 | void ShutDown() { 136 | ShutDown(false); 137 | cout << "shutdown" << endl; 138 | } 139 | 140 | void ShutDownNow() { 141 | ShutDown(true); 142 | cout << "shutdown now" << endl; 143 | } 144 | 145 | private: 146 | void ShutDown(bool is_now) { 147 | if (is_available_.load()) { 148 | if (is_now) { 149 | this->is_shutdown_now_.store(true); 150 | } else { 151 | this->is_shutdown_.store(true); 152 | } 153 | this->task_cv_.notify_all(); 154 | is_available_.store(false); 155 | } 156 | } 157 | 158 | bool IsAvailable() { return is_available_.load(); } 159 | 160 | void AddThread(int id) { AddThread(id, ThreadFlag::kCore); } 161 | 162 | void AddThread(int id, ThreadFlag thread_flag) { 163 | cout << "AddThread " << id << " flag " << static_cast(thread_flag) << endl; 164 | ThreadWrapperPtr thread_ptr = std::make_shared(); 165 | thread_ptr->id.store(id); 166 | thread_ptr->flag.store(thread_flag); 167 | auto func = [this, thread_ptr]() { 168 | for (;;) { 169 | std::function task; 170 | { 171 | ThreadPoolLock lock(this->task_mutex_); 172 | if (thread_ptr->state.load() == ThreadState::kStop) { 173 | break; 174 | } 175 | cout << "thread id " << thread_ptr->id.load() << " running start" << endl; 176 | thread_ptr->state.store(ThreadState::kWaiting); 177 | ++this->waiting_thread_num_; 178 | bool is_timeout = false; 179 | if (thread_ptr->flag.load() == ThreadFlag::kCore) { 180 | this->task_cv_.wait(lock, [this, thread_ptr] { 181 | return (this->is_shutdown_ || this->is_shutdown_now_ || !this->tasks_.empty() || 182 | thread_ptr->state.load() == ThreadState::kStop); 183 | }); 184 | } else { 185 | this->task_cv_.wait_for(lock, this->config_.time_out, [this, thread_ptr] { 186 | return (this->is_shutdown_ || this->is_shutdown_now_ || !this->tasks_.empty() || 187 | thread_ptr->state.load() == ThreadState::kStop); 188 | }); 189 | is_timeout = !(this->is_shutdown_ || this->is_shutdown_now_ || !this->tasks_.empty() || 190 | thread_ptr->state.load() == ThreadState::kStop); 191 | } 192 | --this->waiting_thread_num_; 193 | cout << "thread id " << thread_ptr->id.load() << " running wait end" << endl; 194 | 195 | if (is_timeout) { 196 | thread_ptr->state.store(ThreadState::kStop); 197 | } 198 | 199 | if (thread_ptr->state.load() == ThreadState::kStop) { 200 | cout << "thread id " << thread_ptr->id.load() << " state stop" << endl; 201 | break; 202 | } 203 | if (this->is_shutdown_ && this->tasks_.empty()) { 204 | cout << "thread id " << thread_ptr->id.load() << " shutdown" << endl; 205 | break; 206 | } 207 | if (this->is_shutdown_now_) { 208 | cout << "thread id " << thread_ptr->id.load() << " shutdown now" << endl; 209 | break; 210 | } 211 | thread_ptr->state.store(ThreadState::kRunning); 212 | task = std::move(this->tasks_.front()); 213 | this->tasks_.pop(); 214 | } 215 | task(); 216 | } 217 | cout << "thread id " << thread_ptr->id.load() << " running end" << endl; 218 | }; 219 | thread_ptr->ptr = std::make_shared(std::move(func)); 220 | if (thread_ptr->ptr->joinable()) { 221 | thread_ptr->ptr->detach(); 222 | } 223 | this->worker_threads_.emplace_back(std::move(thread_ptr)); 224 | } 225 | 226 | void Resize(int thread_num) { 227 | if (thread_num < config_.core_threads) return; 228 | int old_thread_num = worker_threads_.size(); 229 | cout << "old num " << old_thread_num << " resize " << thread_num << endl; 230 | if (thread_num > old_thread_num) { 231 | while (thread_num-- > old_thread_num) { 232 | AddThread(GetNextThreadId()); 233 | } 234 | } else { 235 | int diff = old_thread_num - thread_num; 236 | auto iter = worker_threads_.begin(); 237 | while (iter != worker_threads_.end()) { 238 | if (diff == 0) { 239 | break; 240 | } 241 | auto thread_ptr = *iter; 242 | if (thread_ptr->flag.load() == ThreadFlag::kCache && 243 | thread_ptr->state.load() == ThreadState::kWaiting) { // wait 244 | thread_ptr->state.store(ThreadState::kStop); // stop; 245 | --diff; 246 | iter = worker_threads_.erase(iter); 247 | } else { 248 | ++iter; 249 | } 250 | } 251 | this->task_cv_.notify_all(); 252 | } 253 | } 254 | 255 | int GetNextThreadId() { return this->thread_id_++; } 256 | 257 | bool IsValidConfig(ThreadPoolConfig config) { 258 | if (config.core_threads < 1 || config.max_threads < config.core_threads || config.time_out.count() < 1) { 259 | return false; 260 | } 261 | return true; 262 | } 263 | 264 | private: 265 | ThreadPoolConfig config_; 266 | 267 | std::list worker_threads_; 268 | 269 | std::queue> tasks_; 270 | std::mutex task_mutex_; 271 | std::condition_variable task_cv_; 272 | 273 | std::atomic total_function_num_; 274 | std::atomic waiting_thread_num_; 275 | std::atomic thread_id_; 276 | 277 | std::atomic is_shutdown_now_; 278 | std::atomic is_shutdown_; 279 | std::atomic is_available_; 280 | }; 281 | 282 | int main() { 283 | cout << "hello" << endl; 284 | ThreadPool pool(ThreadPool::ThreadPoolConfig{4, 5, 6, std::chrono::seconds(4)}); 285 | pool.Start(); 286 | std::this_thread::sleep_for(std::chrono::seconds(4)); 287 | cout << "thread size " << pool.GetTotalThreadSize() << endl; 288 | std::atomic index; 289 | index.store(0); 290 | std::thread t([&]() { 291 | for (int i = 0; i < 10; ++i) { 292 | pool.Run([&]() { 293 | cout << "function " << index.load() << endl; 294 | std::this_thread::sleep_for(std::chrono::seconds(4)); 295 | index++; 296 | }); 297 | // std::this_thread::sleep_for(std::chrono::seconds(2)); 298 | } 299 | }); 300 | t.detach(); 301 | cout << "=================" << endl; 302 | 303 | std::this_thread::sleep_for(std::chrono::seconds(4)); 304 | pool.Reset(ThreadPool::ThreadPoolConfig{4, 4, 6, std::chrono::seconds(4)}); 305 | std::this_thread::sleep_for(std::chrono::seconds(4)); 306 | cout << "thread size " << pool.GetTotalThreadSize() << endl; 307 | cout << "waiting size " << pool.GetWaitingThreadSize() << endl; 308 | cout << "---------------" << endl; 309 | // pool.ShutDownNow(); 310 | getchar(); 311 | cout << "world" << endl; 312 | return 0; 313 | } 314 | --------------------------------------------------------------------------------