概述
在Linux系统中,线程优先级是影响多线程应用程序性能和响应速度的关键因素之一。通过合理设置线程优先级,可以确保关键任务得到及时处理,同时避免低优先级任务过度占用系统资源。
线程优先级是指操作系统根据一定的规则分配给每个线程的一个数值,用于决定该线程获得CPU时间的顺序和频率。一般来说,优先级越高,线程越有可能被调度执行。相反,优先级较低的线程则需要等待高优先级线程完成之后才能获得CPU时间。然而,这并不意味着低优先级线程永远不会被执行,而是它们的执行机会相对较少。
优先级范围
Linux支持多种线程调度策略,在每种策略下,线程都有其特定的优先级范围。下面,我们将介绍几种常见的调度策略。
1、先入先出(SCHED_FIFO)。一种实时调度策略,适用于对延迟敏感的应用程序。一旦线程开始运行,它会一直占用CPU直到主动放弃或完成任务,不受其他同优先级线程的影响。其优先级范围为1到99,数字越大表示优先级越高。
2、轮转调度(SCHED_RR)。类似于SCHED_FIFO,但为每个线程分配了一个固定的时间片。当一个线程用完其时间片后,即使没有完成工作也会被暂停,让位于下一个同优先级的线程。其优先级范围同样为1到99,数字越大表示优先级越高。
3、完全公平调度器(SCHED_OTHER)。Linux默认的非实时调度策略,适用于大多数普通应用。基于虚拟运行时间来衡量每个线程应当获得多少CPU时间,保证所有线程都能公平地获得CPU资源。其优先级范围为-20到19,可通过nice值调整,默认值为0。nice值越小,优先级越高;nice值越大,优先级越低。
4、批处理调度和低优先级调度(SCHED_BATCH和SCHED_IDLE)。这两个调度策略主要用于后台批处理作业或低优先级服务,其优先级范围通常不需要显式设置。
设置线程优先级
pthread_setschedparam函数用于设置线程调度参数,允许我们指定线程的调度策略和优先级。其函数原型如下。
int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param);
各个参数和返回值的含义如下。
thread:表示要修改调度参数的线程,可通过pthread_self函数获取当前线程的ID,也可使用其他方式获取特定线程的ID。
policy:指定线程的调度策略,可取值包括上面介绍的SCHED_FIFO、SCHED_RR、SCHED_OTHER、SCHED_BATCH、SCHED_IDLE。
param:指向struct sched_param类型的指针,包含线程的调度参数。其主要成员是sched_priority,它定义了线程的优先级。如前所述,对于不同的调度策略,优先级的有效范围也不同。
返回值:成功时返回0,失败时可通过errno获取具体的错误代码。常见的错误代码为:EINVAL(调度策略无效或优先级超出允许范围)、EPERM(没有足够的权限)、ESRCH(没有找到对应的线程)。
在下面的示例代码中,我们创建了两个线程:一个由主线程设置优先级,另一个在其自身的线程函数内设置优先级。主线程创建external_priority_thread线程后,立即设置其为实时优先级30,并执行简单的工作循环。internal_priority_thread线程启动后,自行将其优先级提升至50,并执行类似的工作循环。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>// 在线程外部(主线程中)设置优先级
void* external_priority_thread(void* arg)
{for (int i = 0; i < 5; ++i){printf("External-priority thread working... %d\n", i);sleep(1);}return NULL;
}// 在线程内部设置优先级
void* internal_priority_thread(void* arg)
{// 获取当前线程的调度策略和优先级int policy = 0;struct sched_param param;pthread_getschedparam(pthread_self(), &policy, ¶m);printf("Internal-priority thread initial priority: %d\n", param.sched_priority);// 尝试设置新的高优先级param.sched_priority = 50;pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m);for (int i = 0; i < 5; ++i){printf("Internal-priority thread working... %d\n", i);sleep(1);}return NULL;
}int main()
{// 创建线程,在线程外部(主线程中)设置优先级pthread_t ext_prio_thread;struct sched_param param = { .sched_priority = 30 };pthread_create(&ext_prio_thread, NULL, external_priority_thread, NULL);pthread_setschedparam(ext_prio_thread, SCHED_FIFO, ¶m);// 创建线程,在线程内部设置优先级pthread_t int_prio_thread;pthread_create(&int_prio_thread, NULL, internal_priority_thread, NULL);// 等待两个线程完成pthread_join(ext_prio_thread, NULL);pthread_join(int_prio_thread, NULL);printf("Both threads have completed\n");return 0;
}
注意事项
1、确保设置的优先级在所选调度策略的有效范围内,超出范围可能会导致EINVAL错误。
2、对于实时调度策略(比如:SCHED_FIFO或SCHED_RR),可能需要超级用户权限才能成功设置线程优先级。普通用户创建的线程默认采用SCHED_OTHER策略,并且其优先级受到限制。
3、高优先级线程会抢占低优先级线程的CPU时间,可能导致系统饥饿现象。因此,在设定优先级时,应当谨慎权衡各个线程的重要性。
4、在某些情况下,可以根据应用程序的状态来动态调整线程优先级。比如:当检测到系统负载过高时,适当降低一些非关键线程的优先级,以释放更多资源给更重要的任务。