ThreadPool.h 中线程优先级设置问题
luohaha66 opened this issue · 9 comments
luohaha66 commented
ThreadPool.h 中线程优先级设置问题
设置线程池优先级相关代码
static bool setPriority(Priority priority = PRIORITY_NORMAL, std::thread::native_handle_type threadId = 0) {
// set priority
#if defined(_WIN32)
static int Priorities[] = { THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST };
if (priority != PRIORITY_NORMAL && SetThreadPriority(GetCurrentThread(), Priorities[priority]) == 0) {
return false;
}
return true;
#else
static int Min = sched_get_priority_min(SCHED_OTHER);
if (Min == -1) {
return false;
}
static int Max = sched_get_priority_max(SCHED_OTHER);
if (Max == -1) {
return false;
}
static int Priorities[] = {Min, Min + (Max - Min) / 4, Min + (Max - Min) / 2, Min + (Max - Min) * 3 / 4, Max};
if (threadId == 0) {
threadId = pthread_self();
}
struct sched_param params;
params.sched_priority = Priorities[priority];
return pthread_setschedparam(threadId, SCHED_OTHER, ¶ms) == 0;
#endif
}
存在问题
- SCHED_OTHER 为默认的 Linux 分时调度策略,并不支持优先级的设置,只有 SCHED_FIFO、SCHED_RR 这样的实时调度策略,才支持线程优先级的设置。
- 上述代码中的 Min 以及 Max 其值皆为 0,无论调用者传入的参数 priority 为何值,params.sched_priority 的值都为 0。
xia-chu commented
xia-chu commented
这个代码其实我也是从其他项目扣得
luohaha66 commented
man sched 对 SCHED_OTHER 的解释
- SCHED_OTHER: Default Linux time-sharing scheduling SCHED_OTHER can be used at only static priority 0 (i.e., threads under real-time policies always have priority over SCHED_OTHER processes). SCHED_OTHER is the standard Linux time-sharing scheduler that is intended for all threads that do not require the special real-time mechanisms
测试代码
#include <stdio.h>
#include <sched.h>
int main(void)
{
int Min = sched_get_priority_min(SCHED_OTHER);
int Max = sched_get_priority_max(SCHED_OTHER);
// 输出为 0 0
printf("min %d, max %d\n", Min, Max);
Min = sched_get_priority_min(SCHED_IDLE);
Max = sched_get_priority_max(SCHED_IDLE);
// 输出为 0 0
printf("min %d, max %d\n", Min, Max);
Min = sched_get_priority_min(SCHED_BATCH);
Max = sched_get_priority_max(SCHED_BATCH);
// 输出为 0 0
printf("min %d, max %d\n", Min, Max);
Min = sched_get_priority_min(SCHED_FIFO);
Max = sched_get_priority_max(SCHED_FIFO);
// 输出为 1 99
printf("min %d, max %d\n", Min, Max);
Min = sched_get_priority_min(SCHED_RR);
Max = sched_get_priority_max(SCHED_RR);
// 输出为 1 99
printf("min %d, max %d\n", Min, Max);
return 0;
}
测试平台
uname -a 输出 | cpu | gcc 版本 |
---|---|---|
Linux 5.15.0-86-generic Ubuntu x86_64 GNU/Linux | AMD Ryzen 7 3800X | 11.3 |
Linux 5.10.160 SMP aarch64 GNU/Linux | RK3588 | 10.3 |
Linux 4.19.111 SMP PREEMPT armv7l GNU/Linux | RV1126 | 8.3 |
测试结果
- 我身边没有 Mac 相关环境,就只在 linux 上测试了。
- 一共测了上面 3 个平台,输出都是一样的。
- 从结果来看,linux 和 mac 还真不一样。
xia-chu commented
@luohaha66 那您的建议是怎么修改?换SCHED_RR还是SCHED_FIFO?
luohaha66 commented
@luohaha66 那您的建议是怎么修改?换SCHED_RR还是SCHED_FIFO?
我明天工作时间回复您。
luohaha66 commented
SCHED_RR SCHED_FIFO 如何选择
SCHED_FIFO
- 先进先出调度。
- 只能在静态优先级高于0的情况下使用,这意味着当 SCHED_FIFO 线程变得可运行时,它总是立即抢占当前正在运行的任何 SCHED_OTHER、SCHED_BATCH 或 SCHED_IDLE 线程。
- SCHED_FIFO 线程一直运行到被 I/O 请求阻塞、被高优先级线程抢占或调用 sched_yield 为止。
- SCHED_FIFO 是一种简单的调度算法,没有时间切片。对于 SCHED_FIFO 策略下调度的线程,应用以下规则:
- 被另一个高优先级线程抢占的正在运行的 SCHED_FIFO 线程将保持在其优先级列表的头部,并在所有高优先级线程再次阻塞时立即恢复执行。
- 当阻塞的 SCHED_FIFO 线程变为可运行时,它将被插入到优先级列表的末尾。
SCHED_RR
- 循环调度。
- SCHED_RR 是 SCHED_FIFO 的简单增强。上面为 SCHED_FIFO 描述的所有内容也适用于 SCHED_RR,除了每个线程只允许在最大时间片内运行。
- 如果 SCHED_RR 线程已经运行了等于或大于该时间片的时间段,那么它将被放在优先级列表的末尾。
- 被高优先级线程抢占并随后作为运行线程恢复执行的 SCHED_RR 线程将完成其循环时间量的未过期部分。
分析
-
假设在 cpu0 上存在以下3个线程:
线程名 优先级 调度策略 t0 high FIFO 或 RR t1 mid FIFO 或 RR t3 0 OTHER -
假设 t0 和 t1 使用 FIFO 策略,则只要 t0 线程就绪,就会立刻抢占 t1 和 t3,直到 t0 主动放弃 cpu 为止,t1 或者 t3 才会被运行。
-
假设 t0 和 t1 使用 RR 策略,与 FIFO 的区别就是,t0 线程在使用完时间片以后,会发生一次调度, 此时 t0 仍然是 cpu0 上优先级最高的线程,其会再次获得运行。也就是说,只要 t0 不主动放弃 cpu,其会一直运行。和 FIFO 一致,但是多了额外的开销。
-
假设 t0 和 t1 优先级一致,在使用 FIFO 策略的情况下,如果 t0 先运行,那么 t1 只有等到 t0 主动放弃 cpu,才会得到运行。在使用 RR 策略的情况下,如果 t0 先运行,t1 在 t0 时间片消耗完之后便可得到运行。
SCHED_FIFO 适用情况
- 在设置 cpu 亲和性的情况下,如果在同一个核心上只存在一个同优先级线程,则应使用 SCHED_FIFO。
- 在不设置 cpu 亲和性的情况下,如果同优先级线程数小于或等于 cpu 总核心数,则应使用 SCHED_FIFO。
SCHED_RR 适用情况
- 在设置 cpu 亲和性的情况下,如果在同一个核心上需要绑定一个以上的同优先级线程,则应使用 SCHED_RR。
- 在不设置 cpu 亲和性的情况下,如果同优先级线程数超过 cpu 总核心数,则应使用 SCHED_RR。
建议
- zlmediakit 线程池通常只会创建与 cpu 核心数相等的线程数,并且每个 cpu 核心都只绑定了一个同优先级的线程,推荐使用 SCHED_FIFO 。
xia-chu commented
善哉!那这样修改吧:
Index: src/Thread/ThreadPool.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/Thread/ThreadPool.h b/src/Thread/ThreadPool.h
--- a/src/Thread/ThreadPool.h (revision 3fd2b856b6856dd679a32c673431561c0affdd0c)
+++ b/src/Thread/ThreadPool.h (date 1698292460548)
@@ -86,11 +86,11 @@
}
return true;
#else
- static int Min = sched_get_priority_min(SCHED_OTHER);
+ static int Min = sched_get_priority_min(SCHED_FIFO);
if (Min == -1) {
return false;
}
- static int Max = sched_get_priority_max(SCHED_OTHER);
+ static int Max = sched_get_priority_max(SCHED_FIFO);
if (Max == -1) {
return false;
}
@@ -101,7 +101,7 @@
}
struct sched_param params;
params.sched_priority = Priorities[priority];
- return pthread_setschedparam(threadId, SCHED_OTHER, ¶ms) == 0;
+ return pthread_setschedparam(threadId, SCHED_FIFO, ¶ms) == 0;
#endif
}
luohaha66 commented
谢谢,按照上述修改即可。
xia-chu commented
已提交 感谢!