1#ifndef NEFORCE_CORE_ASYNC_THREAD_POOL_HPP__
2#define NEFORCE_CORE_ASYNC_THREAD_POOL_HPP__
22NEFORCE_BEGIN_NAMESPACE__
44 task_group() =
default;
45 ~task_group() =
default;
68 void wait() const noexcept {
84class NEFORCE_API local_queue {
103 array<function<void()>, queue_size> tasks_;
104 atomic<uint64_t> head_{0};
105 atomic<uint32_t> tail_{0};
108 constexpr static size_t mask_ = queue_size - 1;
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);
114 NEFORCE_NODISCARD
static pair<uint32_t, uint32_t> unpack(
const uint64_t head)
noexcept {
118 uint32_t be_stolen_by_impl(local_queue& dst, uint32_t dst_tail);
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;
132 NEFORCE_NODISCARD
size_t capacity() const noexcept {
return tasks_.size(); }
138 NEFORCE_NODISCARD
bool empty() const noexcept {
return size() == 0U; }
147 const auto steal = unpack(head).first;
148 const auto used =
static_cast<size_t>(tail - steal);
149 const size_t remain =
capacity() - used;
157 NEFORCE_NODISCARD
size_t size() const noexcept {
160 const auto local_head = unpack(head).second;
161 return static_cast<size_t>(tail - local_head);
170 steal_strategy_ = strategy;
171 fixed_batch_size_ = batch_size;
204struct NEFORCE_API worker_context {
240 enum class priority_type :
uint32_t {
298 NEFORCE_NODISCARD
explicit operator bool() const noexcept {
return future.valid() &&
task_info; }
360 static size_t max_thread_threshhold() noexcept;
369 struct priority_task {
377 info(_NEFORCE
move(info)) {}
379 bool operator<(
const priority_task& other)
const noexcept {
return priority < other.priority; }
382 struct thread_pool_id_generator {
383 static NEFORCE_API
uint32_t get_new_id() noexcept;
384 static NEFORCE_API
void reset_id() noexcept;
390 mutex worker_contexts_mtx_;
394 id_type init_thread_size_{0};
395 size_t thread_threshhold_;
397 steal_strategy configured_steal_strategy_{steal_strategy::adaptive};
398 uint32_t configured_steal_batch_{4};
400 unique_ptr<lock_free_queue<shared_ptr<task_type>>> global_queue_;
401 priority_queue<priority_task> priority_queue_;
404 atomic<uint32_t> global_task_count_{0};
405 atomic<uint32_t> idle_thread_size_{0};
406 size_t task_threshhold_{task_max_threshhold};
408 mutex work_available_mtx_;
409 condition_variable work_available_;
410 condition_variable exit_cond_;
412 const vector<sysinfo::numa_node_info>* numa_nodes_{
nullptr};
414 atomic<pool_mode> pool_mode_{pool_mode::fixed};
415 atomic<bool> is_running_{
false};
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};
424 uint64_t generate_task_id() {
return next_task_id_.fetch_add(1, memory_order_relaxed); }
426 void thread_function(id_type thread_id);
427 optional<task_type> try_steal_task(worker_context& ctx);
429 pool_statistics statistics_unsafe()
const;
481 NEFORCE_NODISCARD
bool running() const noexcept {
return is_running_; }
500 bool start(
size_t init_thread_size = 3);
517 template <
typename Func,
typename... Args>
528 template <
typename Func,
typename... Args>
544 template <
typename Func,
typename... Args>
557 template <
typename Func,
typename... Args>
573 template <
typename Func,
typename... Args>
585 template <
typename Func,
typename... Args>
597 token->cancelled.store(
true);
607 template <
typename... Types>
628template <typename Func, typename... Args>
631 static_assert(
is_invocable_v<Func, Args...>,
"Func must be invocable with Args");
639 current_group->increment();
644 group = current_group, info]()
mutable -> Result {
645 struct context_guard {
652 group_inner(
move(g)) {
661 ~context_guard()
noexcept {
670 group_inner->decrement();
679 context_guard guard(info, group);
681 return _NEFORCE
apply(func, args);
684 info->error = e.
what();
688 info->error =
"Unknown exception";
693 auto res =
task->get_future();
694 task_type job([
task] { (*task)(); });
696 if (
static_cast<uint32_t>(priority) > 0) {
699 priority_queue_.emplace(
move(job), priority, info);
701 ++total_submitted_tasks_;
702 work_available_.notify_one();
708 info->error =
"Task queue is full";
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) {
718 ++total_submitted_tasks_;
719 work_available_.notify_one();
723 ++total_submitted_tasks_;
724 work_available_.notify_one();
728 if (pool_mode_.load() == pool_mode::cached) {
731 if (pending > idle) {
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); };
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) {
743 worker_contexts_ptr_.emplace_back(_NEFORCE
move(tmp));
747 auto result = threads_map_.emplace(thread_id, _NEFORCE
move(ptr));
748 result.first->second->start();
749 result.first->second->detach();
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");
762 using Result = invoke_result_t<Func, Args...>;
764 auto info = make_shared<task_info>(generate_task_id(), priority);
766 auto task = _NEFORCE make_shared<packaged_task<Result()>>(
767 [func = _NEFORCE forward<Func>(func), tup = _NEFORCE make_tuple(_NEFORCE forward<Args>(args)...),
769 struct context_guard {
770 shared_ptr<task_info> info;
772 explicit context_guard(shared_ptr<task_info> 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;
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);
787 context_guard guard(info);
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();
798 auto res = task->get_future();
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)(); });
805 return submit_result<Result>{_NEFORCE
move(res), _NEFORCE
move(info)};
808template <
typename Func,
typename... Args>
809thread_pool::periodic_token thread_pool::submit_every(int64_t interval_ms,
const priority_type priority, Func&& func,
811 auto state = make_shared<periodic_task_state>();
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()) {
822 this->submit_task(priority, [task]() { (*task)(); });
824 if (state->cancelled.load()) {
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)(); });
833 auto first_time = steady_clock::now() +
milliseconds(interval_ms);
834 timer_.add_task(first_time, [handler_ptr]() { (*handler_ptr)(); });
873NEFORCE_END_NAMESPACE__
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
获取队列容量
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
窃取策略类型别名
periodic_token submit_every(int64_t interval_ms, Func &&func, Args &&... args)
提交周期性任务(使用默认优先级0)
bool set_mode(pool_mode mode) noexcept
设置线程池模式
pool_statistics stop()
停止线程池
static tuple< future_result_t< Types >... > wait(future< Types > &&... futures)
等待多个future完成
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结果获取函数
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
正在运行的任务计数
bool is_finished() const noexcept
检查任务是否已完成
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)
构造函数
atomic< bool > cancelled
是否已取消
size_t total_completed
总完成任务数
size_t total_submitted
总提交任务数
string to_string() const
转换为字符串
size_t total_stolen
总窃取任务数
void execute(function< void()> handler)
提交 handler 到线程池
bool running_in_this_thread() const noexcept
检查当前线程是否为线程池 worker
atomic< bool > is_stealing
是否正在执行窃取操作
size_t consecutive_idle_count
连续空闲次数
uint32_t numa_node
所属 NUMA 节点编号
uint32_t cpu_core
绑定的 CPU 核心编号