forked from ChunelFeng/CGraph
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUThreadPrimary.h
More file actions
267 lines (223 loc) · 8.03 KB
/
UThreadPrimary.h
File metadata and controls
267 lines (223 loc) · 8.03 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
/***************************
@Author: Chunel
@Contact: chunel@foxmail.com
@File: UThreadPrimary.h
@Time: 2021/7/8 11:02 下午
@Desc: 核心线程,处理任务中
***************************/
#ifndef CGRAPH_UTHREADPRIMARY_H
#define CGRAPH_UTHREADPRIMARY_H
#include <vector>
#include <mutex>
#include "UThreadBase.h"
CGRAPH_NAMESPACE_BEGIN
class UThreadPrimary : public UThreadBase {
protected:
explicit UThreadPrimary() {
index_ = CGRAPH_SECONDARY_THREAD_COMMON_ID;
pool_threads_ = nullptr;
type_ = CGRAPH_THREAD_TYPE_PRIMARY;
}
CStatus init() override {
CGRAPH_FUNCTION_BEGIN
CGRAPH_ASSERT_INIT(false)
CGRAPH_ASSERT_NOT_NULL(config_)
is_init_ = true;
buildStealTargets();
thread_ = std::move(std::thread(&UThreadPrimary::run, this));
setSchedParam();
setAffinity(index_);
CGRAPH_FUNCTION_END
}
/**
* 注册线程池相关内容,需要在init之前使用
* @param index
* @param poolTaskQueue
* @param poolThreads
* @param config
*/
CStatus setThreadPoolInfo(int index,
UAtomicQueue<UTask>* poolTaskQueue,
std::vector<UThreadPrimary *>* poolThreads,
UThreadPoolConfigPtr config) {
CGRAPH_FUNCTION_BEGIN
CGRAPH_ASSERT_INIT(false) // 初始化之前,设置参数
CGRAPH_ASSERT_NOT_NULL(poolTaskQueue, poolThreads, config)
this->index_ = index;
this->pool_task_queue_ = poolTaskQueue;
this->pool_threads_ = poolThreads;
this->config_ = config;
CGRAPH_FUNCTION_END
}
/**
* 线程执行函数
* @return
*/
CStatus run() final {
CGRAPH_FUNCTION_BEGIN
CGRAPH_ASSERT_INIT(true)
CGRAPH_ASSERT_NOT_NULL(pool_threads_)
/**
* 线程池中任何一个primary线程为null都不可以执行
* 防止线程初始化失败的情况,导致的崩溃
* 理论不会走到这个判断逻辑里面
*/
if (std::any_of(pool_threads_->begin(), pool_threads_->end(),
[](UThreadPrimary* thd) {
return nullptr == thd;
})) {
CGRAPH_RETURN_ERROR_STATUS("primary thread is null")
}
status = loopProcess();
CGRAPH_FUNCTION_END
}
CVoid processTask() override {
UTask task;
if (popTask(task) || popPoolTask(task) || stealTask(task)) {
runTask(task);
} else {
fatWait();
}
}
CVoid processTasks() override {
UTaskArr tasks;
if (popTask(tasks) || popPoolTask(tasks) || stealTask(tasks)) {
// 尝试从主线程中获取/盗取批量task,如果成功,则依次执行
runTasks(tasks);
} else {
fatWait();
}
}
/**
* 如果总是进入无task的状态,则开始休眠
* 休眠一定时间后,然后恢复执行状态,避免出现异常情况导致无法唤醒
*/
CVoid fatWait() {
cur_empty_epoch_++;
CGRAPH_YIELD();
if (cur_empty_epoch_ >= config_->primary_thread_busy_epoch_) {
CGRAPH_UNIQUE_LOCK lk(mutex_);
cv_.wait_for(lk, std::chrono::milliseconds(config_->primary_thread_empty_interval_));
cur_empty_epoch_ = 0;
}
}
/**
* 依次push到任一队列里。如果都失败,则yield,然后重新push
* @param task
* @return
*/
CVoid pushTask(UTask&& task) {
while (!(primary_queue_.tryPush(std::move(task))
|| secondary_queue_.tryPush(std::move(task)))) {
CGRAPH_YIELD();
}
cur_empty_epoch_ = 0;
cv_.notify_one();
}
/**
* 从本地弹出一个任务
* @param task
* @return
*/
bool popTask(UTaskRef task) {
return primary_queue_.tryPop(task) || secondary_queue_.tryPop(task);
}
/**
* 从本地弹出一批任务
* @param tasks
* @return
*/
bool popTask(UTaskArrRef tasks) {
CBool result = primary_queue_.tryPop(tasks, config_->max_local_batch_size_);
auto leftSize = config_->max_local_batch_size_ - tasks.size();
if (leftSize > 0) {
// 如果凑齐了,就不需要了。没凑齐的话,就继续
result |= (secondary_queue_.tryPop(tasks, leftSize));
}
return result;
}
/**
* 从其他线程窃取一个任务
* @param task
* @return
*/
bool stealTask(UTaskRef task) {
if (unlikely(pool_threads_->size() < config_->default_thread_size_)) {
/**
* 线程池还未初始化完毕的时候,无法进行steal。
* 确保程序安全运行。
*/
return false;
}
/**
* 窃取的时候,仅从相邻的primary线程中窃取
* 待窃取相邻的数量,不能超过默认primary线程数
*/
for (auto& target : steal_targets_) {
/**
* 从线程中周围的thread中,窃取任务。
* 如果成功,则返回true,并且执行任务。
* steal 的时候,先从第二个队列里偷,从而降低触碰锁的概率
*/
if (likely((*pool_threads_)[target])
&& (((*pool_threads_)[target])->secondary_queue_.trySteal(task))
|| ((*pool_threads_)[target])->primary_queue_.trySteal(task)) {
return true;
}
}
return false;
}
/**
* 从其他线程盗取一批任务
* @param tasks
* @return
*/
bool stealTask(UTaskArrRef tasks) {
if (unlikely(pool_threads_->size() != config_->default_thread_size_)) {
return false;
}
for (auto& target : steal_targets_) {
if (likely((*pool_threads_)[target])) {
bool result = ((*pool_threads_)[target])->secondary_queue_.trySteal(tasks, config_->max_steal_batch_size_);
auto leftSize = config_->max_steal_batch_size_ - tasks.size();
if (leftSize > 0) {
result |= ((*pool_threads_)[target])->primary_queue_.trySteal(tasks, leftSize);
}
if (result) {
/**
* 在这里,我们对模型进行了简化。实现的思路是:
* 尝试从邻居主线程(先secondary,再primary)中,获取 x(=max_steal_batch_size_) 个task,
* 如果从某一个邻居中,获取了 y(<=x) 个task,则也终止steal的流程
* 且如果如果有一次批量steal成功,就认定成功
*/
return true;
}
}
}
return false;
}
/**
* 构造 steal 范围的 target,避免每次盗取的时候,重复计算
* @return
*/
CVoid buildStealTargets() {
steal_targets_.clear();
for (int i = 0; i < config_->calcStealRange(); i++) {
auto target = (index_ + i + 1) % config_->default_thread_size_;
steal_targets_.push_back(target);
}
steal_targets_.shrink_to_fit();
}
private:
int index_; // 线程index
int cur_empty_epoch_ = 0; // 当前空转的轮数信息
UWorkStealingQueue<UTask> primary_queue_; // 内部队列信息
UWorkStealingQueue<UTask> secondary_queue_; // 第二个队列,用于减少触锁概率,提升性能
std::vector<UThreadPrimary *>* pool_threads_; // 用于存放线程池中的线程信息
std::vector<int> steal_targets_; // 被偷的目标信息
friend class UThreadPool;
friend class UAllocator;
};
using UThreadPrimaryPtr = UThreadPrimary *;
CGRAPH_NAMESPACE_END
#endif //CGRAPH_UTHREADPRIMARY_H