同步调度
如果有约束,尤其是时间约束时(即需要某些线程执行得比其他线程快),程序员可以控制线程的执行调度。
像互斥对象这样的 同步对象甚至会阻塞高优先级的线程。 在某些情况下,可能发生称为优先级反转的不期望行为。 线程库 提供互斥协议工具来避免优先级反转。
同步调度定义如何通过拥有互斥对象修改线程的执行调度,尤其是线程的优先级。 这将允许用户定义的行为并避免优先级反转。 当使用复杂的锁定方案时这将是十分有用的。 线程库的某些实现不提供同步调度。
优先级反转
当低优先级的线程拥有互斥对象而阻塞高优先级的线程时将发生优先级反转。 由于它的优先级较低,所以互斥对象所有者可能会在无限的持续时间内拥有该互斥对象。 结果是,不可能保证线程的截止期限。
以下示例举例说明了一个典型的优先级反转。 在该示例中,考虑一个单处理器系统的情况。 优先级反转还以同样的方式发生在多处理器系统中。
pthread_mutex_lock(&M); /* 1 */
...
pthread_mutex_unlock(&M);pthread_mutex_lock(&M); /* 2 */
...
fprintf(...); /* 3 */
...
pthread_mutex_unlock(&M);考虑以下执行时间表。 已调度线程 B 并执行行 2。 执行行 3 时,线程 B 被线程 A抢占先机。 线程 A 执行行 1 并被阻塞,因为互斥对象 M 由线程 B持有。 因此,将调度进程中的其他线程。 由于线程 B 优先级非常低,所以它在很长时间内都不会被重新调度,从而阻塞线程 A(尽管线程 A 有很高的优先级)。
互斥协议
要避免优先级反转,线程库提供以下互斥协议:
- 优先级继承协议
- 有时候称为基本优先级继承协议。 在优先级继承协议中,互斥对象所有者继承具有最高优先级的已阻塞线程 的优先级。 当线程尝试使用该协议锁定互斥对象并且该线程是阻塞的线程,如果已阻塞线程的优先级高于所有者的优先级,那么互斥对象所有者临时 接收已阻塞线程的优先级。 当线程将互斥对象解锁时将恢复其最初的优先级。
- 优先级保护协议
- 有时候称为优先级最高限度协议仿真。 在优先级保护协议中,每个互斥对象都有一个优先级最高限度。 它是优先级的有效范围内的优先级级别。 当线程拥有一个互斥对象时,如果互斥对象的最高 限度高于它自身的优先级,那么该线程临时接收互斥优先级最高限度。 当线程将互斥对象解锁时将恢复其最初的优先级。 优先级最高限度应该 具有可以锁定互斥对象的所有线程的最高优先级的值。 否则,可能发生优先级反转或甚至是死锁,并且该协议将会效率低下。
两个协议都增加了拥有特定互斥对象的线程的优先级,这样就可以保证截止期限。 此外,如果正确使用的话,那么互斥协议还可以防止相互死锁。 分别将互斥协议指定给每个互斥对象。
选择互斥协议
| 值 | 描述 |
|---|---|
| PTHREAD_PRIO_DEFAULT | 无任何值 |
| PTHREAD_PRIO_NONE | 表示没有协议。 |
| PTHREAD_PRIO_INHERIT | 表示优先级继承协议。 |
| PTHREAD_PRIO_PROTECT | 表示优先级保护协议。 |
优先级保护协议使用一个附加属性:prioceiling 属性。 该属性中包含互斥对象的优先级最高限度。 通过 使用 pthread_mutexattr_getprioceiling 和 pthread_mutexattr_setprioceiling 子例程在互斥属性对象中控制 prioceiling 属性。
还可以使用 pthread_mutex_getprioceiling 和 pthread_mutex_setprioceiling 子例程动态控制互斥对象的 prioceiling 属性。 动态更改互斥对象的优先级 最高限度时,该互斥对象由库锁定;互斥对象不能由调用 pthread_mutex_setprioceiling 子例程的线程所有以避免死锁。 增加线程的优先级时动态设置互斥对象的优先级最高限度是十分有用的。
互斥协议的实现是可选的。 每个协议都是一个 POSIX 选项。
继承或保护
两个协议是相似的,它们都会提高拥有互斥对象的线程的优先级。 如果两个协议都可用,那么程序员必须选择一个协议。 选择哪一个协议取决于创建互斥对象的程序员是否可以使用要锁定互斥对象的线程的优先级。 通常情况下,由库定义并且由应用程序线程使用的互斥对象将使用继承协议,而在应用程序内部创建的互斥对象将使用保护协议。
- 使用继承协议时,每次尝试锁定互斥对象时如果线程是锁定的则会进行一次系统调用。
- 使用保护协议时,每次线程锁定互斥对象时始终会进行一次系统调用。
在大多数注重性能的程序中,继承协议应该是可以选择的,因为互斥对象是低争用的对象。 互斥对象很长时间 没有被谁所拥有;所以,线程试图锁定它们的时候不可能被阻塞。