forked from ChunelFeng/CGraph
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUThreadPool.cpp
More file actions
269 lines (212 loc) · 8.36 KB
/
UThreadPool.cpp
File metadata and controls
269 lines (212 loc) · 8.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/***************************
@Author: Chunel
@Contact: chunel@foxmail.com
@File: UThreadPool.cpp
@Time: 2022/10/3 17:31
@Desc:
***************************/
#include "UThreadPool.h"
CGRAPH_NAMESPACE_BEGIN
UThreadPool::UThreadPool(CBool autoInit, const UThreadPoolConfig& config) noexcept {
cur_index_ = 0;
is_init_ = false;
this->setConfig(config); // setConfig 函数,用在 is_init_ 设定之后
if (autoInit) {
this->init();
}
}
UThreadPool::~UThreadPool() {
this->config_.monitor_enable_ = false; // 在析构的时候,才释放监控线程。先释放监控线程,再释放其他的线程
if (monitor_thread_.joinable()) {
monitor_thread_.join();
}
destroy();
}
CStatus UThreadPool::setConfig(const UThreadPoolConfig &config) {
CGRAPH_FUNCTION_BEGIN
CGRAPH_ASSERT_INIT(false) // 初始化后,无法设置参数信息
this->config_ = config;
CGRAPH_FUNCTION_END
}
UThreadPoolConfig UThreadPool::getConfig() const {
return config_;
}
CStatus UThreadPool::init() {
CGRAPH_FUNCTION_BEGIN
if (is_init_) {
CGRAPH_FUNCTION_END
}
if (config_.monitor_enable_) {
// 默认不开启监控线程
monitor_thread_ = std::thread(&UThreadPool::monitor, this);
}
thread_record_map_.clear();
thread_record_map_[(CSize)std::hash<std::thread::id>{}(std::this_thread::get_id())] = CGRAPH_MAIN_THREAD_ID;
task_queue_.setup();
primary_threads_.reserve(config_.default_thread_size_);
for (int i = 0; i < config_.default_thread_size_; i++) {
auto* pt = CGRAPH_SAFE_MALLOC_COBJECT(UThreadPrimary); // 创建核心线程数
pt->setThreadPoolInfo(i, &task_queue_, &primary_threads_, &config_);
// 记录线程和匹配id信息
primary_threads_.emplace_back(pt);
}
/**
* 等待所有thread 设置完毕之后,再进行 init(),
* 避免在个别的平台上,可能出现 thread竞争访问其他线程、并且导致异常的情况
* 参考: https://github.com/ChunelFeng/CGraph/issues/309
*/
for (int i = 0; i < config_.default_thread_size_; i++) {
status += primary_threads_[i]->init();
thread_record_map_[(CSize)std::hash<std::thread::id>{}(primary_threads_[i]->thread_.get_id())] = i;
}
CGRAPH_FUNCTION_CHECK_STATUS
/**
* 策略更新:
* 初始化的时候,也可以创建n个辅助线程。目的是为了配合仅使用 pool中 priority_queue 的场景
* 一般情况下,建议为0。
*/
status = createSecondaryThread(config_.secondary_thread_size_);
CGRAPH_FUNCTION_CHECK_STATUS
is_init_ = true;
CGRAPH_FUNCTION_END
}
CStatus UThreadPool::submit(const UTaskGroup& taskGroup, CMSec ttl) {
CGRAPH_FUNCTION_BEGIN
CGRAPH_ASSERT_INIT(true)
std::vector<std::future<CVoid>> futures;
futures.reserve(taskGroup.getSize());
for (const auto& task : taskGroup.task_arr_) {
futures.emplace_back(commit(task));
}
// 计算最终运行时间信息
auto deadline = std::chrono::steady_clock::now()
+ std::chrono::milliseconds(std::min(taskGroup.getTtl(), ttl));
for (auto& fut : futures) {
const auto& futStatus = fut.wait_until(deadline);
switch (futStatus) {
case std::future_status::ready: break; // 正常情况,直接返回了
case std::future_status::timeout: status += CStatus("thread status timeout"); break;
case std::future_status::deferred: status += CStatus("thread status deferred"); break;
default: status += CStatus("thread status unknown");
}
}
if (taskGroup.on_finished_) {
taskGroup.on_finished_(status);
}
CGRAPH_FUNCTION_END
}
CStatus UThreadPool::submit(CGRAPH_DEFAULT_CONST_FUNCTION_REF func, CMSec ttl,
CGRAPH_CALLBACK_CONST_FUNCTION_REF onFinished) {
return submit(UTaskGroup(func, ttl, onFinished));
}
CIndex UThreadPool::getThreadIndex(CSize tid) {
int index = CGRAPH_SECONDARY_THREAD_COMMON_ID;
auto result = thread_record_map_.find(tid);
if (result != thread_record_map_.end()) {
index = result->second;
}
return index;
}
CStatus UThreadPool::destroy() {
CGRAPH_FUNCTION_BEGIN
if (!is_init_) {
CGRAPH_FUNCTION_END
}
// primary 线程是普通指针,需要delete
for (auto &pt : primary_threads_) {
status += pt->destroy();
}
CGRAPH_FUNCTION_CHECK_STATUS
/**
* 这里之所以 destroy和 delete分开两个循环执行,
* 是因为当前线程被delete后,还可能存在未被delete的主线程,来steal当前线程的任务
* 在windows环境下,可能出现问题。
* destroy 和 delete 分开之后,不会出现此问题。
* 感谢 Ryan大佬(https://github.com/ryanhuang) 提供的帮助
*/
for (auto &pt : primary_threads_) {
CGRAPH_DELETE_PTR(pt)
}
primary_threads_.clear();
// secondary 线程是智能指针,不需要delete
task_queue_.reset();
for (auto &st : secondary_threads_) {
status += st->destroy();
}
CGRAPH_FUNCTION_CHECK_STATUS
secondary_threads_.clear();
thread_record_map_.clear();
is_init_ = false;
CGRAPH_FUNCTION_END
}
CBool UThreadPool::isInit() const {
return is_init_;
}
CStatus UThreadPool::releaseSecondaryThread(CInt size) {
CGRAPH_FUNCTION_BEGIN
// 先将所有已经结束的,给删掉
CGRAPH_LOCK_GUARD lock(st_mutex_);
for (auto iter = secondary_threads_.begin(); iter != secondary_threads_.end(); ) {
!(*iter)->done_ ? secondary_threads_.erase(iter++) : iter++;
}
CGRAPH_RETURN_ERROR_STATUS_BY_CONDITION((size > (CInt)secondary_threads_.size()), \
"cannot release [" + std::to_string(size) + "] secondary thread," \
+ "only [" + std::to_string(secondary_threads_.size()) + "] left.")
// 再标记几个需要删除的信息
for (auto iter = secondary_threads_.begin();
iter != secondary_threads_.end() && size-- > 0; ) {
(*iter)->done_ = false;
iter++;
}
CGRAPH_FUNCTION_END
}
CIndex UThreadPool::dispatch(CIndex origIndex) {
CIndex realIndex = 0;
if (CGRAPH_DEFAULT_TASK_STRATEGY == origIndex) {
realIndex = cur_index_++;
if (cur_index_ >= config_.max_thread_size_ || cur_index_ < 0) {
cur_index_ = 0;
}
} else {
realIndex = origIndex;
}
return realIndex; // 交到上游去判断,走哪个线程
}
CStatus UThreadPool::createSecondaryThread(CInt size) {
CGRAPH_FUNCTION_BEGIN
int leftSize = (int)(config_.max_thread_size_ - config_.default_thread_size_ - secondary_threads_.size());
int realSize = std::min(size, leftSize); // 使用 realSize 来确保所有的线程数量之和,不会超过设定max值
CGRAPH_LOCK_GUARD lock(st_mutex_);
for (int i = 0; i < realSize; i++) {
auto ptr = CGRAPH_MAKE_UNIQUE_COBJECT(UThreadSecondary)
ptr->setThreadPoolInfo(&task_queue_, &priority_task_queue_, &config_);
status += ptr->init();
secondary_threads_.emplace_back(std::move(ptr));
}
CGRAPH_FUNCTION_END
}
CVoid UThreadPool::monitor() {
while (config_.monitor_enable_) {
while (config_.monitor_enable_ && !is_init_) {
// 如果没有init,则一直处于空跑状态
CGRAPH_SLEEP_SECOND(1)
}
auto span = config_.monitor_span_;
while (config_.monitor_enable_ && is_init_ && span--) {
CGRAPH_SLEEP_SECOND(1) // 保证可以快速退出
}
// 如果 primary线程都在执行,则表示忙碌
bool busy = !primary_threads_.empty() && std::all_of(primary_threads_.begin(), primary_threads_.end(),
[](UThreadPrimaryPtr ptr) { return ptr && ptr->is_running_; });
CGRAPH_LOCK_GUARD lock(st_mutex_);
// 如果忙碌或者priority_task_queue_中有任务,则需要添加 secondary线程
if (busy || !priority_task_queue_.empty()) {
createSecondaryThread(1);
}
// 判断 secondary 线程是否需要退出
for (auto iter = secondary_threads_.begin(); iter != secondary_threads_.end(); ) {
(*iter)->freeze() ? secondary_threads_.erase(iter++) : iter++;
}
}
}
CGRAPH_NAMESPACE_END