NexusForce 1.0.0
A rigorously engineered full-stack C++ backend library.
载入中...
搜索中...
未找到
thread_pool.hpp
浏览该文件的文档.
1#ifndef NEFORCE_CORE_ASYNC_THREAD_POOL_HPP__
2#define NEFORCE_CORE_ASYNC_THREAD_POOL_HPP__
3
11
22NEFORCE_BEGIN_NAMESPACE__
23
29
35
43struct task_group {
44 task_group() = default;
45 ~task_group() = default;
46
48
52 void increment() noexcept { running_count.fetch_add(1, memory_order_relaxed); }
53
59 void decrement() noexcept {
60 if (running_count.fetch_sub(1, memory_order_release) == 1) {
61 running_count.notify_all();
62 }
63 }
64
68 void wait() const noexcept {
70 while (count != 0) {
71 running_count.wait(count);
73 }
74 }
75};
76
84class NEFORCE_API local_queue {
85public:
90 enum class steal_strategy : uint8_t {
91 half,
92 fixed_batch,
93 single,
94 adaptive
95 };
96
97 static constexpr size_t queue_size = 256;
98
99private:
101 uint32_t fixed_batch_size_{4};
102
103 array<function<void()>, queue_size> tasks_;
104 atomic<uint64_t> head_{0};
105 atomic<uint32_t> tail_{0};
106
107private:
108 constexpr static size_t mask_ = queue_size - 1;
109
110 NEFORCE_NODISCARD static uint64_t pack(const uint32_t steal, const uint32_t local_head) noexcept {
111 return static_cast<uint64_t>(steal) << 32 | static_cast<uint64_t>(local_head);
112 }
113
114 NEFORCE_NODISCARD static pair<uint32_t, uint32_t> unpack(const uint64_t head) noexcept {
115 return {static_cast<uint32_t>(head >> 32), static_cast<uint32_t>(head)};
116 }
117
118 uint32_t be_stolen_by_impl(local_queue& dst, uint32_t dst_tail);
119
120public:
121 local_queue() = default;
122 ~local_queue() = default;
123 local_queue(const local_queue&) = delete;
124 local_queue& operator=(const local_queue&) = delete;
125 local_queue(local_queue&& other) noexcept;
126 local_queue& operator=(local_queue&& other) noexcept;
127
132 NEFORCE_NODISCARD size_t capacity() const noexcept { return tasks_.size(); }
133
138 NEFORCE_NODISCARD bool empty() const noexcept { return size() == 0U; }
139
144 NEFORCE_NODISCARD size_t remain_size() const noexcept {
145 const auto tail = tail_.load(memory_order_acquire);
146 const auto head = head_.load(memory_order_acquire);
147 const auto steal = unpack(head).first;
148 const auto used = static_cast<size_t>(tail - steal);
149 const size_t remain = capacity() - used;
150 return remain;
151 }
152
157 NEFORCE_NODISCARD size_t size() const noexcept {
158 const auto tail = tail_.load(memory_order_acquire);
159 const auto head = head_.load(memory_order_acquire);
160 const auto local_head = unpack(head).second;
161 return static_cast<size_t>(tail - local_head);
162 }
163
169 void set_steal_strategy(const steal_strategy strategy, const uint32_t batch_size = 4) {
170 steal_strategy_ = strategy;
171 fixed_batch_size_ = batch_size;
172 }
173
178 void push_back(function<void()> task) {
179 const uint32_t tail = tail_.load(memory_order_relaxed);
180 tasks_[tail & mask_] = move(task);
181 tail_.store(tail + 1, memory_order_release);
182 }
183
189
195 optional<function<void()>> be_stolen_by(local_queue& dst_queue);
196};
197
204struct NEFORCE_API worker_context {
206
208 id_type id{0};
213
214 worker_context() = default;
215 worker_context(const worker_context&) = delete;
216 worker_context& operator=(const worker_context&) = delete;
217 worker_context(worker_context&& other) noexcept;
218 worker_context& operator=(worker_context&& other) noexcept;
219};
220
221
228struct task_info {
233 enum class status {
234 pending,
235 running,
236 completed,
237 failed
238 };
239
240 enum class priority_type : uint32_t {
241 };
242
243 const uint64_t id;
249 string error;
250 priority_type priority;
251
257 explicit task_info(const uint64_t task_id, const priority_type priority) :
258 id(task_id),
260
265 NEFORCE_NODISCARD bool is_finished() const noexcept {
266 const auto s = status.load(memory_order_acquire);
267 return s == status::completed || s == status::failed;
268 }
269
274 NEFORCE_NODISCARD int64_t exec_time() const noexcept {
275 if (start_time.value() == 0 || finish_time.value() == 0) {
276 return -1;
277 }
278 return finish_time.value() - start_time.value();
279 }
280};
281
289template <typename T>
291 _NEFORCE future<T> future;
293
298 NEFORCE_NODISCARD explicit operator bool() const noexcept { return future.valid() && task_info; }
299};
300
301
313class NEFORCE_API thread_pool {
314public:
319 enum class pool_mode : uint8_t {
321 cached
322 };
323
331
336 struct NEFORCE_API pool_statistics : istringify<pool_statistics> {
340 size_t queue_size;
344
349 NEFORCE_NODISCARD string to_string() const;
350 };
351
355 using priority_type = task_info::priority_type;
356
358 static constexpr size_t max_idle_seconds = 60;
359
360 static size_t max_thread_threshhold() noexcept;
361
362private:
363 using task_type = function<void()>;
364
369 struct priority_task {
370 task_type task;
371 priority_type priority;
373
374 priority_task(task_type t, const priority_type p, shared_ptr<task_info> info) noexcept :
375 task(move(t)),
376 priority(p),
377 info(_NEFORCE move(info)) {}
378
379 bool operator<(const priority_task& other) const noexcept { return priority < other.priority; }
380 };
381
382 struct thread_pool_id_generator {
383 static NEFORCE_API uint32_t get_new_id() noexcept;
384 static NEFORCE_API void reset_id() noexcept;
385 };
386
387 unordered_map<id_type, unique_ptr<lazy_thread>> threads_map_;
388 unordered_map<id_type, worker_context> worker_contexts_;
389 vector<atomic<worker_context*>> worker_contexts_ptr_;
390 mutex worker_contexts_mtx_;
391
393
394 id_type init_thread_size_{0};
395 size_t thread_threshhold_;
396
397 steal_strategy configured_steal_strategy_{steal_strategy::adaptive};
398 uint32_t configured_steal_batch_{4};
399
400 unique_ptr<lock_free_queue<shared_ptr<task_type>>> global_queue_;
401 priority_queue<priority_task> priority_queue_;
402 mutex priority_mtx_;
403
404 atomic<uint32_t> global_task_count_{0};
405 atomic<uint32_t> idle_thread_size_{0};
406 size_t task_threshhold_{task_max_threshhold};
407
408 mutex work_available_mtx_;
409 condition_variable work_available_;
410 condition_variable exit_cond_;
411
412 const vector<sysinfo::numa_node_info>* numa_nodes_{nullptr};
413
414 atomic<pool_mode> pool_mode_{pool_mode::fixed};
415 atomic<bool> is_running_{false};
416
417 atomic<size_t> total_submitted_tasks_{0};
418 atomic<size_t> total_completed_tasks_{0};
419 atomic<size_t> total_stolen_tasks_{0};
420 atomic<size_t> steal_worker_count_{0};
421 atomic<uint64_t> next_task_id_{0};
422
423private:
424 uint64_t generate_task_id() { return next_task_id_.fetch_add(1, memory_order_relaxed); }
425
426 void thread_function(id_type thread_id);
427 optional<task_type> try_steal_task(worker_context& ctx);
428
429 pool_statistics statistics_unsafe() const;
430
431public:
436
441
442 thread_pool(const thread_pool&) = delete;
443 thread_pool& operator=(const thread_pool&) = delete;
444
445 thread_pool(thread_pool&&) = delete;
446 thread_pool& operator=(thread_pool&&) = delete;
447
453 bool set_mode(pool_mode mode) noexcept;
454
461 bool set_steal_mode(steal_strategy strategy, uint32_t steal_batch = 4) noexcept;
462
468 bool set_task_threshhold(size_t threshhold) noexcept;
469
475 bool set_thread_threshhold(size_t threshhold) noexcept;
476
481 NEFORCE_NODISCARD bool running() const noexcept { return is_running_; }
482
487 NEFORCE_NODISCARD pool_mode mode() const noexcept { return pool_mode_; }
488
493 NEFORCE_NODISCARD pool_statistics statistics() const;
494
500 bool start(size_t init_thread_size = 3);
501
507
517 template <typename Func, typename... Args>
518 submit_result<invoke_result_t<Func, Args...>> submit_task(priority_type priority, Func&& func, Args&&... args);
519
528 template <typename Func, typename... Args>
529 submit_result<invoke_result_t<Func, Args...>> submit_task(Func&& func, Args&&... args) {
530 return this->submit_task(static_cast<priority_type>(0), _NEFORCE forward<Func>(func),
531 _NEFORCE forward<Args>(args)...);
532 }
533
544 template <typename Func, typename... Args>
545 submit_result<invoke_result_t<Func, Args...>> submit_after(int64_t delay_ms, priority_type priority, Func&& func,
546 Args&&... args);
547
557 template <typename Func, typename... Args>
558 submit_result<invoke_result_t<Func, Args...>> submit_after(int64_t delay_ms, Func&& func, Args&&... args) {
559 return this->submit_after(delay_ms, static_cast<priority_type>(0), _NEFORCE forward<Func>(func),
560 _NEFORCE forward<Args>(args)...);
561 }
562
573 template <typename Func, typename... Args>
574 periodic_token submit_every(int64_t interval_ms, priority_type priority, Func&& func, Args&&... args);
575
585 template <typename Func, typename... Args>
586 periodic_token submit_every(int64_t interval_ms, Func&& func, Args&&... args) {
587 return this->submit_every(interval_ms, static_cast<priority_type>(0), _NEFORCE forward<Func>(func),
588 _NEFORCE forward<Args>(args)...);
589 }
590
595 static void cancel_periodic_task(const periodic_token& token) {
596 if (token) {
597 token->cancelled.store(true);
598 }
599 }
600
607 template <typename... Types>
608 static tuple<future_result_t<Types>...> wait(future<Types>&&... futures) {
609 return _NEFORCE make_tuple(_NEFORCE get(futures)...);
610 }
611};
612
613
618NEFORCE_API worker_context*& get_worker_context() noexcept;
619
625
627
628template <typename Func, typename... Args>
629submit_result<invoke_result_t<Func, Args...>> thread_pool::submit_task(const priority_type priority, Func&& func,
630 Args&&... args) {
631 static_assert(is_invocable_v<Func, Args...>, "Func must be invocable with Args");
632
633 using Result = invoke_result_t<Func, Args...>;
634
635 auto info = make_shared<task_info>(generate_task_id(), priority);
636
637 const auto current_group = get_current_task_group();
638 if (current_group) {
639 current_group->increment();
640 }
641
642 auto task = _NEFORCE make_shared<packaged_task<Result()>>(
643 [func = _NEFORCE forward<Func>(func), args = _NEFORCE make_tuple(_NEFORCE forward<Args>(args)...),
644 group = current_group, info]() mutable -> Result {
645 struct context_guard {
647 shared_ptr<task_group> group_inner;
648 shared_ptr<task_group> prev_group_inner;
649
650 explicit context_guard(shared_ptr<task_info> i, shared_ptr<task_group> g) :
651 info(move(i)),
652 group_inner(move(g)) {
654 info->start_time = timestamp::now();
655 info->worker_thread_id = get_worker_context() ? get_worker_context()->id : 0;
656
657 prev_group_inner = get_current_task_group();
658 get_current_task_group() = group_inner;
659 }
660
661 ~context_guard() noexcept {
662 try {
663 info->finish_time = timestamp::now();
664 auto expected = task_info::status::running;
665 info->status.compare_exchange_strong(expected, task_info::status::completed,
667
668 get_current_task_group() = prev_group_inner;
669 if (group_inner) {
670 group_inner->decrement();
671 }
672 // NOLINTNEXTLINE(bugprone-empty-catch)
673 } catch (...) {
674 /* ignore */
675 }
676 }
677 };
678
679 context_guard guard(info, group);
680 try {
681 return _NEFORCE apply(func, args);
682 } catch (const exception& e) {
684 info->error = e.what();
685 throw;
686 } catch (...) {
688 info->error = "Unknown exception";
689 throw;
690 }
691 });
692
693 auto res = task->get_future();
694 task_type job([task] { (*task)(); });
695
696 if (static_cast<uint32_t>(priority) > 0) {
697 {
698 lock<mutex> lk(priority_mtx_);
699 priority_queue_.emplace(move(job), priority, info);
700 }
701 ++total_submitted_tasks_;
702 work_available_.notify_one();
703 } else {
704 auto* ctx = get_worker_context();
705
706 if (global_task_count_.load(memory_order_acquire) >= task_threshhold_) {
708 info->error = "Task queue is full";
709 return submit_result<Result>{_NEFORCE move(res), _NEFORCE move(info)};
710 }
711
712 if (ctx != nullptr && ctx->queue.remain_size() > 0) {
713 ctx->queue.push_back(move(job));
714 ++total_submitted_tasks_;
715 } else if (ctx == nullptr) {
716 global_task_count_.fetch_add(1, memory_order_release);
717 global_queue_->push(make_shared<task_type>(move(job)));
718 ++total_submitted_tasks_;
719 work_available_.notify_one();
720 } else {
721 global_task_count_.fetch_add(1, memory_order_release);
722 global_queue_->push(make_shared<task_type>(move(job)));
723 ++total_submitted_tasks_;
724 work_available_.notify_one();
725 }
726 }
727
728 if (pool_mode_.load() == pool_mode::cached) {
729 const uint32_t idle = idle_thread_size_.load(memory_order_acquire);
730 const uint32_t pending = global_task_count_.load(memory_order_acquire);
731 if (pending > idle) {
732 lock<mutex> lk(worker_contexts_mtx_);
733 if (threads_map_.size() < thread_threshhold_) {
734 id_type thread_id = thread_pool_id_generator::get_new_id();
735 auto worker_func = [this, thread_id]() { thread_function(thread_id); };
736 auto ptr = _NEFORCE make_unique<lazy_thread>(_NEFORCE move(worker_func));
737
738 if (thread_id >= worker_contexts_ptr_.size()) {
739 worker_contexts_ptr_.reserve(thread_id + 1);
740 for (size_t i = worker_contexts_ptr_.size(); i <= thread_id; ++i) {
742 tmp.store(nullptr, memory_order_relaxed);
743 worker_contexts_ptr_.emplace_back(_NEFORCE move(tmp));
744 }
745 }
746
747 auto result = threads_map_.emplace(thread_id, _NEFORCE move(ptr));
748 result.first->second->start();
749 result.first->second->detach();
750 }
751 }
752 }
753
754 return submit_result<Result>{_NEFORCE move(res), _NEFORCE move(info)};
755}
756
757template <typename Func, typename... Args>
758submit_result<invoke_result_t<Func, Args...>>
759thread_pool::submit_after(const int64_t delay_ms, const priority_type priority, Func&& func, Args&&... args) {
760 static_assert(is_invocable_v<Func, Args...>, "Func must be invocable with Args");
761
762 using Result = invoke_result_t<Func, Args...>;
763
764 auto info = make_shared<task_info>(generate_task_id(), priority);
765
766 auto task = _NEFORCE make_shared<packaged_task<Result()>>(
767 [func = _NEFORCE forward<Func>(func), tup = _NEFORCE make_tuple(_NEFORCE forward<Args>(args)...),
768 info]() mutable {
769 struct context_guard {
770 shared_ptr<task_info> info;
771
772 explicit context_guard(shared_ptr<task_info> i) :
773 info(move(i)) {
774 info->status.store(task_info::status::running, memory_order_release);
775 info->start_time = timestamp::now();
776 info->worker_thread_id = get_worker_context() ? get_worker_context()->id : 0;
777 }
778
779 ~context_guard() noexcept {
780 info->finish_time = timestamp::now();
781 auto expected = task_info::status::running;
782 info->status.compare_exchange_strong(expected, task_info::status::completed,
783 memory_order_release);
784 }
785 };
786
787 context_guard guard(info);
788
789 try {
790 return _NEFORCE apply(func, tup);
791 } catch (const exception& e) {
792 info->status.store(task_info::status::failed, memory_order_release);
793 info->error = e.what();
794 throw;
795 }
796 });
797
798 auto res = task->get_future();
799
800 auto expire_time = steady_clock::now() + milliseconds(delay_ms);
801 timer_.add_task(expire_time, [this, task = _NEFORCE move(task), priority]() mutable {
802 this->submit_task(priority, [task]() { (*task)(); });
803 });
804
805 return submit_result<Result>{_NEFORCE move(res), _NEFORCE move(info)};
806}
807
808template <typename Func, typename... Args>
809thread_pool::periodic_token thread_pool::submit_every(int64_t interval_ms, const priority_type priority, Func&& func,
810 Args&&... args) {
811 auto state = make_shared<periodic_task_state>();
812 auto task = _NEFORCE make_shared<function<void()>>(
813 [func = _NEFORCE forward<Func>(func),
814 tup = _NEFORCE make_tuple(_NEFORCE forward<Args>(args)...)]() mutable { _NEFORCE apply(func, tup); });
815 auto handler_ptr = _NEFORCE make_shared<task_type>();
816 weak_ptr<task_type> weak_handler(handler_ptr);
817 *handler_ptr = [this, state, task, interval_ms, priority, weak_handler]() {
818 if (state->cancelled.load()) {
819 return;
820 }
821
822 this->submit_task(priority, [task]() { (*task)(); });
823
824 if (state->cancelled.load()) {
825 return;
826 }
827 if (auto locked = weak_handler.lock()) {
828 auto next_time = steady_clock::now() + milliseconds(interval_ms);
829 timer_.add_task(next_time, [locked]() { (*locked)(); });
830 }
831 };
832
833 auto first_time = steady_clock::now() + milliseconds(interval_ms);
834 timer_.add_task(first_time, [handler_ptr]() { (*handler_ptr)(); });
835 return state;
836}
837
839 // ThreadPool
841
846
854 thread_pool* pool;
855
860 void execute(function<void()> handler) { pool->submit_task(move(handler)); }
861
866 NEFORCE_NODISCARD bool running_in_this_thread() const noexcept { return get_worker_context() != nullptr; }
867};
868 // Executor
870 // AsyncComponents
872
873NEFORCE_END_NAMESPACE__
874#endif // NEFORCE_CORE_ASYNC_THREAD_POOL_HPP__
函数包装器主模板声明
独占future类模板
延迟启动线程类
线程本地任务队列
bool empty() const noexcept
检查队列是否为空
static constexpr size_t queue_size
队列容量
void set_steal_strategy(const steal_strategy strategy, const uint32_t batch_size=4)
设置窃取策略
void push_back(function< void()> task)
推送任务到队列尾部
size_t size() const noexcept
获取队列当前大小
size_t remain_size() const noexcept
获取剩余容量
optional< function< void()> > be_stolen_by(local_queue &dst_queue)
被其他队列窃取任务
optional< function< void()> > try_pop()
从队列头部弹出任务
size_t capacity() const noexcept
获取队列容量
steal_strategy
任务窃取策略
锁管理器模板
非递归互斥锁
static constexpr T max() noexcept
获取类型的最大值
共享智能指针类模板
task_info::priority_type priority_type
优先级类型别名
submit_result< invoke_result_t< Func, Args... > > submit_task(Func &&func, Args &&... args)
提交任务(使用默认优先级0)
submit_result< invoke_result_t< Func, Args... > > submit_after(int64_t delay_ms, priority_type priority, Func &&func, Args &&... args)
提交延迟任务
pool_mode mode() const noexcept
获取线程池模式
static constexpr size_t max_idle_seconds
最大空闲秒数
local_queue::steal_strategy steal_strategy
窃取策略类型别名
thread_pool()
默认构造函数
periodic_token submit_every(int64_t interval_ms, Func &&func, Args &&... args)
提交周期性任务(使用默认优先级0)
uint32_t id_type
线程ID类型别名
pool_mode
线程池运行模式
bool set_mode(pool_mode mode) noexcept
设置线程池模式
pool_statistics stop()
停止线程池
static tuple< future_result_t< Types >... > wait(future< Types > &&... futures)
等待多个future完成
~thread_pool()
析构函数
shared_ptr< periodic_task_state > periodic_token
周期性任务令牌
bool set_steal_mode(steal_strategy strategy, uint32_t steal_batch=4) noexcept
设置窃取策略
periodic_token submit_every(int64_t interval_ms, priority_type priority, Func &&func, Args &&... args)
提交周期性任务
bool set_task_threshhold(size_t threshhold) noexcept
设置任务队列阈值
submit_result< invoke_result_t< Func, Args... > > submit_task(priority_type priority, Func &&func, Args &&... args)
提交任务
bool set_thread_threshhold(size_t threshhold) noexcept
设置线程数阈值
static void cancel_periodic_task(const periodic_token &token)
取消周期性任务
static constexpr size_t task_max_threshhold
最大任务队列阈值
bool running() const noexcept
检查线程池是否正在运行
bool start(size_t init_thread_size=3)
启动线程池
pool_statistics statistics() const
获取线程池统计信息
submit_result< invoke_result_t< Func, Args... > > submit_after(int64_t delay_ms, Func &&func, Args &&... args)
提交延迟任务(使用默认优先级0)
定时任务调度器
static timestamp now() noexcept
获取当前时间戳
独占智能指针
动态大小数组容器
日期时间处理库
constexpr T && forward(remove_reference_t< T > &x) noexcept
完美转发左值
enable_if_t< is_void_v< T >, future_result_t< T > > get(future< T > &f)
通用future结果获取函数
long int64_t
64位有符号整数类型
unsigned int uint32_t
32位无符号整数类型
unsigned long uint64_t
64位无符号整数类型
unsigned char uint8_t
8位无符号整数类型
constexpr iter_difference_t< Iterator > count(Iterator first, Iterator last, const T &value)
统计范围内等于指定值的元素数量
duration< int64_t, milli > milliseconds
毫秒持续时间
typename inner::__invoke_result_aux< F, Args... >::type invoke_result_t
invoke_result的便捷别名
constexpr bool is_invocable_v
is_invocable的便捷变量模板
constexpr auto memory_order_release
释放内存顺序常量
constexpr auto memory_order_acquire
获取内存顺序常量
constexpr auto memory_order_relaxed
宽松内存顺序常量
enable_if_t<!is_unbounded_array_v< T > &&is_constructible_v< T, Args... >, shared_ptr< T > > make_shared(Args &&... args)
融合分配创建共享指针
constexpr Iterator2 move(Iterator1 first, Iterator1 last, Iterator2 result) noexcept(noexcept(inner::__move_aux(first, last, result)))
移动范围元素
int priority() noexcept
获取线程优先级
worker_context *& get_worker_context() noexcept
获取当前线程的工作线程上下文
shared_ptr< task_group > & get_current_task_group() noexcept
获取当前线程的任务组
constexpr decltype(auto) apply(Func &&f, Tuple &&t) noexcept(inner::__apply_unpack_tuple< _NEFORCE is_nothrow_invocable, Func, Tuple >::value)
将元组元素解包作为参数调用函数
constexpr tuple< unwrap_ref_decay_t< Types >... > make_tuple(Types &&... args)
从参数创建元组
constexpr unique_ptr< T > make_unique(Args &&... args)
创建unique_ptr
延迟启动线程实现
function< float(float)> function
缓动函数类型:输入归一化时间 [0,1],输出进度 [0,1]
可选值类型
优先队列容器适配器
队列容器适配器
通用原子类型模板
void store(T value, const memory_order mo=memory_order_seq_cst) noexcept
原子存储操作
virtual const char * what() const noexcept
获取错误信息
可字符串化接口
_NEFORCE future< T > future
任务的future
shared_ptr< _NEFORCE task_info > task_info
任务信息
void decrement() noexcept
减少运行计数
void increment() noexcept
增加运行计数
void wait() const noexcept
等待组内所有任务完成
atomic< size_t > running_count
正在运行的任务计数
const uint64_t id
任务ID
bool is_finished() const noexcept
检查任务是否已完成
status
任务状态枚举
int64_t exec_time() const noexcept
获取任务执行时间
uint32_t worker_thread_id
执行任务的线程ID
timestamp submit_time
提交时间
timestamp finish_time
完成时间
timestamp start_time
开始执行时间
priority_type priority
任务优先级
task_info(const uint64_t task_id, const priority_type priority)
构造函数
string error
错误信息
string to_string() const
转换为字符串
thread_pool 的轻量执行器适配器
void execute(function< void()> handler)
提交 handler 到线程池
bool running_in_this_thread() const noexcept
检查当前线程是否为线程池 worker
工作线程上下文
local_queue queue
本地任务队列
atomic< bool > is_stealing
是否正在执行窃取操作
size_t consecutive_idle_count
连续空闲次数
uint32_t id_type
线程ID类型
uint32_t numa_node
所属 NUMA 节点编号
uint32_t cpu_core
绑定的 CPU 核心编号
系统信息查询工具
异步定时器
无序映射容器
弱智能指针实现