Linux默认SCHED_OTHER策略下跨平台线程优先级兼容方案问询
背景与初始实现
我正尝试实现一个跨平台的线程优先级调整封装。在POSIX系统中,最初的实现方案如下:
/// set the priority adjustment for the current thread /// @param pri priority between -2 and 2; 0 is the "normal" priority /// @return true if successful bool czu::set_thread_priority(int pri) { struct sched_param sp; int policy; pthread_t self = pthread_self(); if (pthread_getschedparam(self, &policy, &sp) != 0) return false; int pmin = sched_get_priority_min(policy); int pmax = sched_get_priority_max(policy); if (pmin == -1 || pmax == -1) return false; sp.sched_priority = pmin + (pmax-pmin) * (pri + 2) / 4; return pthread_setschedparam(self, policy, &sp) == 0; }
该方案在macOS上可正常工作(线程优先级范围为[15,47],默认值31),但在Linux默认SCHED_OTHER调度策略下,允许的优先级范围为[0,0],无法实现优先级调整。
切换至SCHED_FIFO或SCHED_RR策略虽可调整优先级,但这类策略会使线程始终优先于SCHED_OTHER任务,仅适用于特定场景,不符合需求。
基于nice值的替代方案
我发现使用nice值(通过nice(2)/setpriority(2))可实现Linux线程优先级调整,当前Linux/NPTL实现中nice值为线程级属性(与POSIX标准规定的进程级属性不同,这源于Linux中线程与进程的模糊边界),因此实现了如下方案:
bool czu::set_thread_priority(int pri) { if (pri < -2) pri = -2; if (pri > 2) pri = 2; #ifdef _WIN32 // %pri was conveniently chosen so that it matches SetThreadPriority return SetThreadPriority(GetCurrentThread(), pri); #elif __linux__ // On Linux the pthread_setschedparam stuff works only with particular // scheduling policies, specifically it does not work with the default one; // instead, nice(2)/setpriority(2) works, and affects only the current // thread instead of doing what POSIX would say (whole process). // Here the allowed range is [20, -19] (20 = minimum priority); we can // ignore clamping -20 as setpriority is already guaranteed to do it. return setpriority(PRIO_PROCESS, 0, pri*-10) == 0; #else struct sched_param sp; int policy; pthread_t self = pthread_self(); if (pthread_getschedparam(self, &policy, &sp) != 0) return false; int pmin = sched_get_priority_min(policy); int pmax = sched_get_priority_max(policy); if (pmin == -1 || pmax == -1) return false; sp.sched_priority = pmin + (pmax-pmin) * (pri + 2) / 4; return pthread_setschedparam(self, policy, &sp) == 0; #endif }
但查阅man 2 setpriority发现:
According to POSIX, the nice value is a per-process setting. However, under the current Linux/NPTL implementation of POSIX threads, the nice value is a per-thread attribute: different threads in the same process can have different nice values. Portable applications should avoid relying on the Linux behavior, which may be made standards conformant in the future.
即Linux当前的nice值线程级特性未来可能被修改以符合POSIX标准,存在兼容性风险。
当前的Fallback兼容方案
为此我提出了一种 fallback 兼容方案:
bool czu::set_thread_priority(int pri) { if (pri < -2) pri = -2; if (pri > 2) pri = 2; #ifdef _WIN32 // %pri was conveniently chosen so that it matches SetThreadPriority return SetThreadPriority(GetCurrentThread(), pri); #else struct sched_param sp; int policy; pthread_t self = pthread_self(); // on POSIX the admissible priorities range isn't fixed, and depends from // the scheduler policy for the thread; let's figure all this out if (pthread_getschedparam(self, &policy, &sp) != 0) return false; int pmin = sched_get_priority_min(policy); int pmax = sched_get_priority_max(policy); if (pmin == -1 || pmax == -1) return false; #ifdef __linux__ if (pmax - pmin == 0) { // On Linux the pthread_setschedparam stuff works only with particular // scheduling policies, specifically it does not work with the default // one; instead, nice(2)/setpriority(2) works, and affects only the // current thread instead of doing what POSIX would say (whole process). // Here the allowed range is [20, -19] (20 = minimum priority); we can // ignore clamping -20 as setpriority is already guaranteed to do it. return setpriority(PRIO_PROCESS, 0, pri*-10) == 0; } #endif // plain linear interpolation sp.sched_priority = pmin + (pmax-pmin) * (pri + 2) / 4; return pthread_setschedparam(self, policy, &sp) == 0; #endif }
请问是否有更优的API或实现方案,能在保证未来兼容性的前提下,实现Linux默认SCHED_OTHER策略下的线程优先级调整?
内容的提问来源于stack exchange,提问作者Matteo Italia

