Linux SCHED_FIFO 不尊重线程优先级
Linux SCHED_FIFO not respecting thread priorities
场景
我创建了三个线程,固定到 单核 ,在 SCHED_FIFO
下具有以下优先级:
- 主要:
sched_priority = 99
- thread_1:
sched_priority = 97
- thread_2:
sched_priority = 98
工作线程 (thread_1
,thread_2
) 计算 50,000,000 个素数的总和 (~ 10s)。他们不会阻塞或执行系统调用直到结束(打印输出)。
主线程休眠一秒钟,然后检查工作线程的承诺以查看是否完成。
预期行为
主线程的优先级最高。根据 sched:
A SCHED_FIFO thread runs until either it is blocked by an I/O
request, it is preempted by a higher priority thread, or it calls
sched_yield(2).
因此,Main 应该以秒为间隔打印 (checking ...
)。它具有最高优先级,因此应该抢占任何 运行ning。当它休眠时,它是阻塞的,所以其他线程应该 运行.
thread_1
:先完成,因为当main不忙时它有优先权。
thread_2
:最后完成,仅在 thread_1
完成后才开始。
实际行为
线程以与预期相反的顺序完成:
Thread 1 summed 3001134 primes at priority level: 97
Thread 2 summed 3001134 primes at priority level: 98
Main: Checking ...
Main: Task 1 has finished!
Main: Task 2 has finished!
Main: Exiting at priority level: 99
颠倒优先顺序,使 main 具有最低的产生完全相同的结果。
重现
- 用
g++ -o <exec_name> <file_name>.cpp -pthread
编译程序
- 运行 与:
sudo taskset --cpu-list 1 ./<exec_name>
我的内核是 5.4.0-42-generic
,我的发行版(如果重要的话):Ubuntu 18.04.5 LTS
。我没有安装preempt-rt
补丁。
类似问题
我发现 this 问题似乎描述了相同的问题,但没有给出答案。
我也看过this question说我的高优先级线程是可以被抢占的,不过这个我不管,只要不被其他线程抢占就行同样的过程。关于这是否会发生,我没有足够的信息。
示例代码
#include <thread>
#include <mutex>
#include <iostream>
#include <chrono>
#include <cstring>
#include <future>
#include <pthread.h>
#include <math.h>
// IO Access mutex
std::mutex g_mutex_io;
// Computation function (busy work)
static bool isPrime (unsigned int value)
{
unsigned int i, root;
if (value == 1) return false;
if (value == 2) return true;
if ((value % 2) == 0) return false;
root = (int)(1.0 + sqrt(value));
for (i = 3; (i < root) && (value % i != 0); i += 2);
return (i < root ? false : true);
}
// Thread function
void foo (unsigned int id, unsigned int count)
{
sched_param sch;
int policy, sum = 0;
// Get information about thread
pthread_getschedparam(pthread_self(), &policy, &sch);
// Compute primes
for (unsigned int i = 1; i < count; ++i) {
sum += (isPrime(i) ? 1 : 0);
}
// Print
{
std::lock_guard<std::mutex> lock(g_mutex_io);
std::cout << "Thread " << id << " summed " << sum << " primes"
<< " at priority level: " << sch.sched_priority << std::endl;
}
}
int main ()
{
sched_param sch;
int policy;
// Declare and init task objects
std::packaged_task<void(unsigned int, unsigned int)> task_1(foo);
std::packaged_task<void(unsigned int, unsigned int)> task_2(foo);
// Get the futures
auto task_fut_1 = task_1.get_future();
auto task_fut_2 = task_2.get_future();
// Declare and init thread objects
std::thread thread_1(std::move(task_1), 1, 50000000);
std::thread thread_2(std::move(task_2), 2, 50000000);
// Set first thread policy
pthread_getschedparam(thread_1.native_handle(), &policy, &sch);
sch.sched_priority = 97;
if (pthread_setschedparam(thread_1.native_handle(), SCHED_FIFO, &sch)) {
std::cerr << "pthread_setschedparam: " << std::strerror(errno)
<< std::endl;
return -1;
}
// Set second thread policy
pthread_getschedparam(thread_2.native_handle(), &policy, &sch);
sch.sched_priority = 98;
if (pthread_setschedparam(thread_2.native_handle(), SCHED_FIFO, &sch)) {
std::cerr << "pthread_setschedparam: " << std::strerror(errno)
<< std::endl;
return -1;
}
// Set main process thread priority
pthread_getschedparam(pthread_self(), &policy, &sch);
sch.sched_priority = 99;
if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sch)) {
std::cerr << "pthread_setschedparam: " << std::strerror(errno)
<< std::endl;
return -1;
}
// Detach these threads
thread_1.detach(); thread_2.detach();
// Check their status with a timeout
for (int finished = 0; finished < 2; ) {
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lock(g_mutex_io);
std::cout << "Main: Checking ..." << std::endl;
}
if (task_fut_1.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
{
std::lock_guard<std::mutex> lock(g_mutex_io);
std::cout << "Main: Task 1 has finished!" << std::endl;
}
finished++;
}
if (task_fut_2.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
{
std::lock_guard<std::mutex> lock(g_mutex_io);
std::cout << "Main: Task 2 has finished!" << std::endl;
}
finished++;
}
}
pthread_getschedparam(pthread_self(), &policy, &sch);
std::cout << "Main: Exiting at priority level: " << sch.sched_priority << std::endl;
return 0;
}
实验
运行将此程序与两个内核 sudo taskset --cpu-list 1,2
结合使用会导致以下奇怪的输出:
Thread 2 computed 3001134 primes at priority level: 98
Thread 1 computed 3001134 primes at priority level: 0
Main: Checking ...
Main: Task 1 has finished!
Main: Task 2 has finished!
Main: Exiting at priority level: 99
thread_1
的优先级为零。
如果我将其扩展为包括三个内核 sudo taskset --cpu-list 1,2,3
,那么我将在单核上得到我期望的行为:
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Thread 2 computed 3001134 primes at priority level: 98
Thread 1 computed 3001134 primes at priority level: 0
Main: Checking ...
Main: Task 1 has finished!
Main: Task 2 has finished!
Main: Exiting at priority level: 99
重新排列优先级的配置顺序,使主线程首先完成,不会改变原始场景中的输出
当你启动两个线程时
// Declare and init thread objects
std::thread thread_1(std::move(task_1), 1, 50000000);
std::thread thread_2(std::move(task_2), 2, 50000000);
他们可能 (!) 立即 运行 并获取计划参数
// Get information about thread
pthread_getschedparam(pthread_self(), &policy, &sch);
甚至在您使用 pthread_setschedparam()
将它们设置为另一个值之前。
如果相应地安排了两个线程,输出甚至可能显示 0 和 0。
子线程可能 (!) 都在主线程设置优先级后进行调度。然后你会得到预期的输出。但任何结果都是可能的。
当您将 pthread_getschedparam()
移动到输出之前的线程末尾时,您更有可能获得 97 和 98 的预期输出。但即便如此,两个线程也可能 运行 直到结束,甚至在主线程被调度之前设置优先级。
场景
我创建了三个线程,固定到 单核 ,在 SCHED_FIFO
下具有以下优先级:
- 主要:
sched_priority = 99
- thread_1:
sched_priority = 97
- thread_2:
sched_priority = 98
工作线程 (thread_1
,thread_2
) 计算 50,000,000 个素数的总和 (~ 10s)。他们不会阻塞或执行系统调用直到结束(打印输出)。
主线程休眠一秒钟,然后检查工作线程的承诺以查看是否完成。
预期行为
主线程的优先级最高。根据 sched:
因此,A SCHED_FIFO thread runs until either it is blocked by an I/O request, it is preempted by a higher priority thread, or it calls sched_yield(2).
Main 应该以秒为间隔打印 (checking ...
)。它具有最高优先级,因此应该抢占任何 运行ning。当它休眠时,它是阻塞的,所以其他线程应该 运行.
thread_1
:先完成,因为当main不忙时它有优先权。thread_2
:最后完成,仅在thread_1
完成后才开始。
实际行为
线程以与预期相反的顺序完成:
Thread 1 summed 3001134 primes at priority level: 97
Thread 2 summed 3001134 primes at priority level: 98
Main: Checking ...
Main: Task 1 has finished!
Main: Task 2 has finished!
Main: Exiting at priority level: 99
颠倒优先顺序,使 main 具有最低的产生完全相同的结果。
重现
- 用
g++ -o <exec_name> <file_name>.cpp -pthread
编译程序
- 运行 与:
sudo taskset --cpu-list 1 ./<exec_name>
我的内核是 5.4.0-42-generic
,我的发行版(如果重要的话):Ubuntu 18.04.5 LTS
。我没有安装preempt-rt
补丁。
类似问题
我发现 this 问题似乎描述了相同的问题,但没有给出答案。
我也看过this question说我的高优先级线程是可以被抢占的,不过这个我不管,只要不被其他线程抢占就行同样的过程。关于这是否会发生,我没有足够的信息。
示例代码
#include <thread>
#include <mutex>
#include <iostream>
#include <chrono>
#include <cstring>
#include <future>
#include <pthread.h>
#include <math.h>
// IO Access mutex
std::mutex g_mutex_io;
// Computation function (busy work)
static bool isPrime (unsigned int value)
{
unsigned int i, root;
if (value == 1) return false;
if (value == 2) return true;
if ((value % 2) == 0) return false;
root = (int)(1.0 + sqrt(value));
for (i = 3; (i < root) && (value % i != 0); i += 2);
return (i < root ? false : true);
}
// Thread function
void foo (unsigned int id, unsigned int count)
{
sched_param sch;
int policy, sum = 0;
// Get information about thread
pthread_getschedparam(pthread_self(), &policy, &sch);
// Compute primes
for (unsigned int i = 1; i < count; ++i) {
sum += (isPrime(i) ? 1 : 0);
}
// Print
{
std::lock_guard<std::mutex> lock(g_mutex_io);
std::cout << "Thread " << id << " summed " << sum << " primes"
<< " at priority level: " << sch.sched_priority << std::endl;
}
}
int main ()
{
sched_param sch;
int policy;
// Declare and init task objects
std::packaged_task<void(unsigned int, unsigned int)> task_1(foo);
std::packaged_task<void(unsigned int, unsigned int)> task_2(foo);
// Get the futures
auto task_fut_1 = task_1.get_future();
auto task_fut_2 = task_2.get_future();
// Declare and init thread objects
std::thread thread_1(std::move(task_1), 1, 50000000);
std::thread thread_2(std::move(task_2), 2, 50000000);
// Set first thread policy
pthread_getschedparam(thread_1.native_handle(), &policy, &sch);
sch.sched_priority = 97;
if (pthread_setschedparam(thread_1.native_handle(), SCHED_FIFO, &sch)) {
std::cerr << "pthread_setschedparam: " << std::strerror(errno)
<< std::endl;
return -1;
}
// Set second thread policy
pthread_getschedparam(thread_2.native_handle(), &policy, &sch);
sch.sched_priority = 98;
if (pthread_setschedparam(thread_2.native_handle(), SCHED_FIFO, &sch)) {
std::cerr << "pthread_setschedparam: " << std::strerror(errno)
<< std::endl;
return -1;
}
// Set main process thread priority
pthread_getschedparam(pthread_self(), &policy, &sch);
sch.sched_priority = 99;
if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sch)) {
std::cerr << "pthread_setschedparam: " << std::strerror(errno)
<< std::endl;
return -1;
}
// Detach these threads
thread_1.detach(); thread_2.detach();
// Check their status with a timeout
for (int finished = 0; finished < 2; ) {
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lock(g_mutex_io);
std::cout << "Main: Checking ..." << std::endl;
}
if (task_fut_1.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
{
std::lock_guard<std::mutex> lock(g_mutex_io);
std::cout << "Main: Task 1 has finished!" << std::endl;
}
finished++;
}
if (task_fut_2.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
{
std::lock_guard<std::mutex> lock(g_mutex_io);
std::cout << "Main: Task 2 has finished!" << std::endl;
}
finished++;
}
}
pthread_getschedparam(pthread_self(), &policy, &sch);
std::cout << "Main: Exiting at priority level: " << sch.sched_priority << std::endl;
return 0;
}
实验
运行将此程序与两个内核 sudo taskset --cpu-list 1,2
结合使用会导致以下奇怪的输出:
Thread 2 computed 3001134 primes at priority level: 98
Thread 1 computed 3001134 primes at priority level: 0
Main: Checking ...
Main: Task 1 has finished!
Main: Task 2 has finished!
Main: Exiting at priority level: 99
thread_1
的优先级为零。
如果我将其扩展为包括三个内核 sudo taskset --cpu-list 1,2,3
,那么我将在单核上得到我期望的行为:
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Main: Checking ...
Thread 2 computed 3001134 primes at priority level: 98
Thread 1 computed 3001134 primes at priority level: 0
Main: Checking ...
Main: Task 1 has finished!
Main: Task 2 has finished!
Main: Exiting at priority level: 99
重新排列优先级的配置顺序,使主线程首先完成,不会改变原始场景中的输出
当你启动两个线程时
// Declare and init thread objects
std::thread thread_1(std::move(task_1), 1, 50000000);
std::thread thread_2(std::move(task_2), 2, 50000000);
他们可能 (!) 立即 运行 并获取计划参数
// Get information about thread
pthread_getschedparam(pthread_self(), &policy, &sch);
甚至在您使用 pthread_setschedparam()
将它们设置为另一个值之前。
如果相应地安排了两个线程,输出甚至可能显示 0 和 0。
子线程可能 (!) 都在主线程设置优先级后进行调度。然后你会得到预期的输出。但任何结果都是可能的。
当您将 pthread_getschedparam()
移动到输出之前的线程末尾时,您更有可能获得 97 和 98 的预期输出。但即便如此,两个线程也可能 运行 直到结束,甚至在主线程被调度之前设置优先级。