调度线程

可以调度线程,线程库提供多个工具来处理并控制对线程的调度。

它还提供在同步操作(例如,锁定互斥 对象)期间控制线程调度的工具。 每个线程都有其自己的一组调度参数。 这些参数可以在线程被创建之前使用线程属性对象设置。 还可以 在线程执行期间动态设置这些参数。

控制线程的调度是一个复杂的任务。 因为调度程序会处理系统范围内的所有线程,所以线程的调度参数与该进程以及其他进程中所有其他线程的调度参数进行交互。 如果想要控制线程的调度,请首先使用以下工具。

线程库允许程序员用以下方式控制线程调度的执行:
  • 通过在创建线程时设置调度属性
  • 通过动态更改已创建线程的调度属性
  • 通过在创建互斥对象时定义线程调度上的互斥对象的影响(称为同步调度
  • 通过在同步操作期间动态更改线程的调度(称为同步调度

调度参数

一个线程具有以下调度参数:

参数 描述
作用域 (scope) 线程的争用作用域是由在线程库中使用的线程模型定义的。
策略 线程的调度策略定义了调度程序获取对 CPU 的控制后如何处理线程。
优先级 (priority) 线程的调度优先级定义了每个线程所完成的工作的相对重要性。

可以在线程创建之前或线程执行期间设置调度参数。 通常,控制线程的调度参数仅对于 CPU 消耗高的线程是十分重要的。 所以,线程库 提供的缺省值对于大多数情况都是够用的。

使用 inheritsched 属性

线程属性对象的 inheritsched 属性指定如何定义线程的调度属性。 以下值是有效的:

描述
PTHREAD_INHERIT_SCHED 指定新线程将获取正在创建的线程的调度属性(schedpolicyschedparam 属 性)。 在属性对象中定义的调度属性将被忽略。
PTHREAD_EXPLICIT_SCHED 指定新线程将获取在该属性对象中定义的调度属性。

inheritsched 属性的缺省值是 PTHREAD_INHERIT_SCHED。 通过调 用 pthread_attr_setinheritsched 子例程设置该属性。 通过调 用 pthread_attr_getinheritsched 子例程返回属性的当前值。

要在线程属性对象中设置线程的调度属性,必须首先将 inheritsched 属性设为PTHREAD_EXPLICIT_SCHED。 否则,将忽略属性对象调度属性。

调度策略和优先级

线程库提供以下调度策略:

描述
SCHED_FIFO 先进先出 (FIFO) 调度。 每个线程都有一个固定的优先级;当多个线程具有相同的优先级时,它们按照先进先出 (FIFO) 的顺序运行直到完成。
SCHED_RR 循环 (RR) 调度。 每个线程都有固定的优先级;当多个线程具有相同的优先级时,它们按照先进先出 (FIFO) 的顺序在一个 固定的时间片内运行。
SCHED_OTHER 默认AIX调度。 每个线程都有一个由调度程序根据线程的活动动态修改的初始优先级;线程的执行是按时间分割的。 在其他系统上,此调度策略可能会不同。

在 5.3之前的 AIX 版本中,不允许在将线程的调度策略设置为 SCHED_OTHER 时更改其优先级。 在这种情况下,内核直接管理优先级,并且可传递给 pthread_setschedparam 子例程的唯一合法值为 DEFAULT_PRIO 值。 DEFAULT_PRIO 值在 pthread.h 文件中定义为 1,并且传递的任何其他值将被忽略。

AIX 5.3开始,可以在将线程的调度策略设置为 SCHED_OTHER时更改其优先级。 可传递给 pthread_setschedparam 子例程的合法值为 40 到 80,但是仅特权用户可设置高于 60 的优先级。 1 至 39 范围内的优先级提供与 40 一样的优先级,而 81 至 127 范围内的优先级提供与 80 一样的优先级。

注:AIX中,内核会反转优先级。 对于 AIX 内核,优先级在 0 到 127 的范围内,其中 0 是最优先的优先级, 127 是最不优先的优先级。 像 ps 这样的命令用于报告内核优先级。
线程库通过 sched_param 结构处理优先级,该结构定义 在 sys/sched.h 头文件中。 该结构包含以下字段:
字段 描述
sched_priority 指定优先级。
sched_policy 该字段被线程库忽略。 请不要使用。

在创建时设置调度策略和优先级

通过设置线程属性对象的 schedpolicy 属性来创建线程时可以设置调度策略。 pthread_attr_setschedpolicy 子例程将调度策略设为先前定义的调度策略中的一个。 线程属性对象的 schedpolicy 属性的当前值可通过使用 pthread_attr_getschedpolicy 子例程获得。

通过设置线程属性对象的 schedpolicy 属性创建线程时,可以设置调度优先级。 pthread_attr_setschedparam 子例程设置 schedparam 属性的值,复制指定结构的值。 pthread_attr_getschedparam 子例程获取 schedparam 属性。

在以下代码片段中,将创建带有循环调度策略的线程,使用优先级 3:
sched_param schedparam;

schedparam.sched_priority = 3;

pthread_attr_init(&attr);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr, SCHED_RR);
pthread_attr_setschedparam(&attr, &schedparam);

pthread_create(&thread, &attr, &start_routine, &args);
pthread_attr_destroy(&attr);
有关 inheritsched 属性的更多信息,请参阅 使用 inheritsched 属性

在执行时设置调度属性

pthread_getschedparam 子例程返回线程的 schedpolicyschedparam 属性。 这些属性可以通过调 用 pthread_setschedparam 子例程来设置。 如果目标线程当前正在处理器上运行,那么在下次调度线程时实现新的调度策略和优先级。 如果目标线程还未运行,那么可以在子例程调用的最后立即被调度。

例如,在 T 的 schedpolicy 属性更改 为 FIFO 时请考虑当前正使用循环策略运行的线程 T。 T 将运行到其时间片结束为止,此时将对它的调度属性进行重新求值。 如果没有线程具有更高的优先级,即使前面其他线程具有相同的 优先级,也将对 T 进行重新调度。 考虑第二个示例,其中低优先权的线程未在运行。 如果该线程的优先级被另一个调用 pthread_setschedparam 子例程的线程升高,如果目标线程是最高优先级的可运行线程,那么立即对目标线程进行调度。

注: 两个子例程都使用 policy 参数和 sched_param 结构。 虽然此结构包含sched_policy字段,程序不应该使用该字段。 子例程使用 policy 参数来传递调度策略,然后子例程忽略sched_policy:NONE.

调度策略注意事项

应用程序应该使用缺省调度策略,除非特定应用程序要求使用固定优先级的调度策略。 使用非缺省策略时请考虑以下各点:
  • 使用循环策略可以确保具有相同优先级的所有线程都能平等地被调度,而不管它们的活动是什么。 在线程必须读取传感器或写入动臂结构 时,这一点在程序中将是十分有用的。
  • 必须非常小心地使用 FIFO 策略。 运行 FIFO 策略的线程将一直运行到结束,除非被某些调用阻塞,例如执行输入和输出操作。 高优先 级的 FIFO 线程不能被占先,并且还会影响系统的全局性能。 例如,像反转一个大型矩阵这样的进行大量计算的线程,永远不能 与 FIFO 策略一起运行。

调度策略和优先级的设置还受线程的争用作用域影响。 并不是始终允许使用 FIFO 或循环策略。

sched_yield subroutine

sched_yield 子例程等价于 yield 子例程的线程。 sched_yield 子例程强制调用线程放弃对其处理器的使用并为其他线程提供进行调度的机会。 下一个已调度的线程可以与调用线程属于相同的进程或属于另一个进程。 请不要在多线程程序中使用 yield 子例程。

单一 UNIX 规范》第 2 版中没有接口pthread_yield子程序。