线程调度:策略、问题与解决方案
1. 线程调度基础
在多线程系统中,线程对共享资源的竞争会引发一系列调度问题。当低优先级线程获取了共享资源后,只要该线程持有资源,尝试获取同一资源的高优先级线程就会被阻塞。优先级继承协议(PIP)和优先级天花板协议(PCP)的目标是限制这种阻塞的持续时间,防止中等优先级线程在低优先级线程释放资源之前抢占(从而延迟)低优先级线程。
共享资源的仲裁通过关联的互斥锁实现。当资源可用时,互斥锁解锁;当资源被使用时,互斥锁锁定。线程通过内核调用尝试锁定互斥锁来请求独占访问。如果互斥锁已被锁定,尝试失败,线程将进入挂起状态,直到互斥锁解锁。否则,内核锁定互斥锁并允许线程继续执行,此时线程被认为拥有该互斥锁,直到后续调用内核解锁。
2. 优先级协议
2.1 优先级天花板协议(PCP)
PCP规定,线程在获取共享资源时立即提高其优先级,并在释放资源时恢复到原始值。临时优先级是程序员预先确定的访问同一资源的所有线程中的最高优先级,称为优先级天花板。
PCP的实现方式有两种:
-
线程源代码实现
:在获取资源前调用内核库函数提高优先级,释放资源后调用函数降低优先级。
-
内核实现
:在创建互斥锁时将天花板值作为调用参数提供,并由内核维护在关联的数据结构中。这种方式更优,因为除了初始的互斥锁创建外,PCP对应用程序是透明的。
PCP的缺点是无论是否有高优先级线程被阻塞,都会提高优先级,可能会不必要地延迟其他线程的执行。而且,当有多个线程访问资源时,将优先级提高到天花板值可能过于严格。
2.2 优先级继承协议(PIP)
PIP在内核代码中实现互斥锁的锁定和解锁操作。当线程尝试锁定互斥锁失败时,内核会查找拥有该互斥锁的线程。如果所有者的优先级低于当前被阻塞的线程,内核会临时提高所有者的优先级,使其与被阻塞线程的优先级匹配,直到资源被释放。
PIP对线程的源代码是透明的,因为它完全在内核中实现。与PCP相比,PIP的系统整体响应时间性能更好。PIP仅在低优先级线程开始阻塞高优先级线程时提高其优先级,因此仅在必要时延迟其他线程的执行。而且,当需要提高优先级时,PIP只将其提高到被阻塞线程的优先级,该优先级可能低于天花板值。
以下是两种协议的对比表格:
| 协议 | 实现位置 | 优先级提升时机 | 优先级提升幅度 | 对系统响应时间影响 |
| ---- | ---- | ---- | ---- | ---- |
| PCP | 线程源代码或内核 | 获取资源时 | 到优先级天花板 | 可能不必要延迟其他线程 |
| PIP | 内核 | 低优先级线程阻塞高优先级线程时 | 到被阻塞线程优先级 | 仅必要时延迟其他线程 |
3. 优先级分配策略
3.1 传统多用户系统的调度策略
在传统多用户系统中,通常采用最短任务优先的策略来运行计算任务,以最小化平均总体响应时间,让用户对系统性能满意。例如,有两个用户同时提交请求,一个需要1秒CPU时间,另一个需要6秒。如果先运行长任务,响应时间分别为6秒和7秒,一个用户会被显著延迟;如果先运行短任务,响应时间分别为1秒和7秒,两个用户都会满意。
3.2 实时系统的优先级分配
3.2.1 截止日期驱动调度
对于有响应时间截止日期的实时系统,最短任务优先策略可能不合适。每个任务都有两个相关时间:一是由CPU速度和计算复杂度决定的计算响应所需的最短时间,二是外部约束允许的最长时间(截止日期)。
例如,有两个同时启动的任务,一个是2秒任务,截止日期为7秒;另一个是4秒任务,截止日期为5秒。只有先运行较长的(4秒)任务,才能满足两个任务的截止日期。这种调度方式称为截止日期驱动调度,它优先调度截止日期最早的线程。
3.2.2 速率单调调度
对于具有固定执行时间的周期性任务,可以使用速率单调分析(RMA)来分析确定给定系统是否能满足其截止日期。RMA的主要结果是,执行周期最短的任务被分配最高优先级,而不考虑每个任务的重要性。这种分配方式有助于确保执行速度较快的任务满足其截止日期,防止被执行速度较慢的任务抢占。
例如,处理串行数据包到达的任务,其周期与串行数据速率相关。随着速率增加,完整数据包更频繁地组装并准备好处理,即连续数据包之间的周期减小,根据RMA,该任务的优先级应更高。
RMA还能估计处理器利用率,这是衡量处理器能力使用程度的指标,可指示是否需要更快的处理器或较慢(且更便宜)的处理器是否足够。一般来说,应尽量将利用率保持在80%以下,因为随着处理器利用率的增加,任务无法满足其截止日期的概率也会增加。
以下是不同调度策略的对比流程图:
graph LR
A[传统多用户系统] --> B[最短任务优先]
C[实时系统] --> D[截止日期驱动调度]
C --> E[速率单调调度]
4. 死锁问题
4.1 死锁的定义和示例
死锁是指两个或多个线程因等待彼此的资源而被阻塞的情况。只有当每个线程同时需要对两个或多个共享资源进行独占访问时,才会发生死锁。
例如,有两个线程T1和T2共享两个资源R1和R2。线程T1锁定了互斥锁M1以使用资源R1,线程T2锁定了互斥锁M2以使用资源R2。当T2尝试锁定M1时,由于M1已被T1锁定,内核将T2置于挂起状态。T1继续运行并尝试锁定M2,由于M2已被T2锁定,内核也将T1置于挂起状态。此时,两个线程都被阻塞,无法完成其关键部分以释放锁,从而导致死锁。
4.2 死锁的检测和解决
随着线程数量的增加,识别潜在死锁情况的能力会迅速下降。不过,有开发工具可以自动分析软件并检测此类情况,但仍需要程序员找到解决问题的方法。
需要注意的是,当通过关闭任务切换或禁用中断来保护关键部分时,死锁是不可能发生的。但这些技术仅适用于短关键部分,对于长关键部分,使用互斥锁是在不影响其他任务响应时间的情况下保护关键部分的唯一合理方法。
解决死锁的策略有:
-
按相同顺序获取资源
:要求所有线程按相同顺序获取资源。但选择的具体顺序是任意的,可能会导致资源被锁定的时间比必要的更长,而且有时找不到适合所有线程的顺序。
-
释放资源并重试
:如果某个线程无法获取某个资源,则要求其释放所有已获取的资源并重新开始。
-
死锁检测算法
:虽然存在检测死锁是否发生的算法,但在大多数实时系统中,检测到死锁后打破死锁并不实际。
以下是死锁发生的流程表格:
| 事件 | 线程T1状态 | 线程T2状态 | 资源R1状态 | 资源R2状态 |
| ---- | ---- | ---- | ---- | ---- |
| 初始 | 运行 | 运行 | 被T1锁定 | 被T2锁定 |
| T2尝试锁定M1 | 运行 | 挂起(等待M1) | 被T1锁定 | 被T2锁定 |
| T1尝试锁定M2 | 挂起(等待M2) | 挂起(等待M1) | 被T1锁定 | 被T2锁定 |
5. 看门狗定时器
5.1 看门狗定时器的作用
优先级反转和死锁可能导致实时系统失败。尽管有避免这些问题的策略,但由于成本、复杂性或疏忽,这些策略有时未被实施。即使实施了,系统仍可能因编程错误、硬件故障或意外情况而失败。
如果嵌入式应用的连续运行至关重要,且故障是暂时的,则可以使用看门狗定时器提供基本的恢复机制。看门狗定时器是一个以固定速率递减计数的硬件计数器。软件应定期重启计数器,使其永远不会实际达到零。如果计数器达到零,则认为发生了故障,看门狗硬件会向CPU发送复位信号,使系统重新初始化。在安全关键应用中,看门狗硬件还必须负责将所有设备置于安全状态,因为CPU本身可能已经失效。
5.2 看门狗定时器的实现策略
在实时多线程应用中,看门狗定时器不仅要验证CPU是否在执行指令,还必须确保任务满足其截止日期。一种有效的软件策略是为每个任务关联一个整数表示的时间(“截止日期”)和两个布尔标志(“忙碌”和“失败”)。在系统初始化时,所有这些标志都被清除为false。
当事件触发任务执行时,事件计算任务的未来截止日期,将其记录在整数中,将“忙碌”标志设置为true,然后调用内核释放挂起的任务。任务完成输出响应计算后,如果错过截止日期,则将“失败”标志设置为true,并无条件地将“忙碌”标志清除为false。
看门狗软件定期检查每个任务。如果某个任务的“失败”标志为true,或者“忙碌”标志为true且当前时间大于其公布的截止日期,则认为该任务错过截止日期。如果所有任务都没有失败,看门狗软件将重启硬件计数器。这些检查相对简单且计算速度快,通常可以作为相对不频繁但高优先级的任务运行,对系统的其他部分影响不大。
以下是看门狗定时器监控任务的代码示例:
// 任务触发部分
task_trigger() {
deadline[TASK] = current_time() + LIMIT;
busy[TASK] = true;
SemaphorePost(...);
}
// 任务执行部分
while (true) {
SemaphorePend(...);
// 计算输出响应
if (current_time() > deadline[TASK]) {
failed[TASK] = true;
}
busy[TASK] = false;
}
// 看门狗任务部分
while (true) {
int now, task;
sleep(...); // 小于计数器周期
now = current_time();
for (task = 0; task < tasks; task++) {
if (failed[task]) break;
if (busy[task] && now > deadline[task]) break;
}
if (task == tasks) restart_counter();
}
综上所述,线程调度是多线程系统中的关键问题,涉及优先级协议、调度策略、死锁处理和看门狗定时器等多个方面。合理选择和应用这些技术对于确保系统的实时性能和稳定性至关重要。
6. 常见问题解答与案例分析
6.1 常见问题解答
以下是一些关于线程调度的常见问题及解答:
1.
在有界优先级反转期间,哪个线程拥有共享资源?
- 答案是低优先级线程。在有界优先级反转中,低优先级线程先获取了共享资源,高优先级线程在其持有资源期间会被阻塞。
2.
有界优先级反转何时开始?
- 当高优先级线程开始等待(挂起)共享资源时,有界优先级反转开始。此时低优先级线程已经获取了该资源。
3.
有界优先级反转何时结束?
- 当低优先级线程释放共享资源时,有界优先级反转结束。此时高优先级线程可以获取资源并继续执行。
4.
三个线程(低、中、高优先级)之间的无界优先级反转需要哪些线程共享资源?
- 需要低优先级和高优先级线程共享资源。无界优先级反转的关键在于低优先级线程阻塞高优先级线程,而中等优先级线程的介入会使情况更加复杂。
5.
哪种解决无界优先级反转的方案能最小化低优先级线程优先级提升的时间?
- 优先级继承协议(PIP)能最小化低优先级线程优先级提升的时间。PIP仅在低优先级线程阻塞高优先级线程时提高其优先级,且只提高到被阻塞线程的优先级。
6.
考虑一个由三个线程组成的多线程应用。初始时,低优先级线程正在运行并拥有共享资源,高优先级线程正在等待同一资源。在低优先级线程释放资源之前,中等优先级线程准备好运行。在不同协议下,高优先级线程的最大等待时间是多少?
-
固定优先级协议
:高优先级线程的最大等待时间为低优先级线程完成其关键部分的最小时间(L)加上中等优先级线程完成其任务的最小时间(M),即 L + M。
-
优先级天花板协议(PCP)
:高优先级线程的最大等待时间为低优先级线程完成其关键部分的最小时间(L),因为PCP会提高低优先级线程的优先级,避免中等优先级线程抢占。
-
优先级继承协议(PIP)
:高优先级线程的最大等待时间为低优先级线程完成其关键部分的最小时间(L),因为PIP会在低优先级线程阻塞高优先级线程时提高其优先级。
7.
在不同协议下,中等优先级线程的最大等待时间由什么决定?
-
固定优先级协议
:中等优先级线程的最大等待时间取决于高优先级线程完成其关键部分的最小时间(H)和低优先级线程完成其关键部分的最小时间(L)。如果高优先级线程先执行,中等优先级线程需要等待 H;如果低优先级线程先执行且阻塞了高优先级线程,中等优先级线程需要等待 L + H。
-
优先级天花板协议(PCP)
:中等优先级线程的最大等待时间取决于高优先级线程完成其关键部分的最小时间(H)。因为PCP会提高低优先级线程的优先级,避免中等优先级线程抢占。
-
优先级继承协议(PIP)
:中等优先级线程的最大等待时间取决于高优先级线程完成其关键部分的最小时间(H)。因为PIP会在低优先级线程阻塞高优先级线程时提高其优先级。
8.
在不同协议下,低优先级线程完成其关键部分的最大时间由什么决定?
-
固定优先级协议
:低优先级线程完成其关键部分的最大时间取决于中等优先级线程完成其任务的最小时间(M)和高优先级线程完成其关键部分的最小时间(H)。如果中等优先级线程抢占了低优先级线程,低优先级线程需要等待 M;如果高优先级线程需要该资源,低优先级线程需要等待 H。
-
优先级天花板协议(PCP)
:低优先级线程完成其关键部分的最大时间为其自身完成关键部分的最小时间(L),因为PCP会提高低优先级线程的优先级,避免中等优先级线程抢占。
-
优先级继承协议(PIP)
:低优先级线程完成其关键部分的最大时间为其自身完成关键部分的最小时间(L),因为PIP会在低优先级线程阻塞高优先级线程时提高其优先级。
9.
在不同协议下,中等优先级线程完成其任务的最大时间由什么决定?
-
固定优先级协议
:中等优先级线程完成其任务的最大时间取决于高优先级线程完成其关键部分的最小时间(H)和低优先级线程完成其关键部分的最小时间(L)。如果高优先级线程先执行,中等优先级线程需要等待 H;如果低优先级线程先执行且阻塞了高优先级线程,中等优先级线程需要等待 L + H。
-
优先级天花板协议(PCP)
:中等优先级线程完成其任务的最大时间取决于高优先级线程完成其关键部分的最小时间(H)。因为PCP会提高低优先级线程的优先级,避免中等优先级线程抢占。
-
优先级继承协议(PIP)
:中等优先级线程完成其任务的最大时间取决于高优先级线程完成其关键部分的最小时间(H)。因为PIP会在低优先级线程阻塞高优先级线程时提高其优先级。
10.
如何通过完成线程状态序列来展示线程如何陷入死锁?
- 以下是一个示例:
| 事件 | 线程A状态 | 线程B状态 | 资源1所有者 | 资源2所有者 |
| ---- | ---- | ---- | ---- | ---- |
| 开始 | 运行 | 就绪 | 线程A | 无 |
| 事件1 | 运行 | 运行 | 线程A | 线程B |
| 事件2 | 挂起(等待资源2) | 运行 | 线程A | 线程B |
| 事件3 | 挂起(等待资源2) | 挂起(等待资源1) | 线程A | 线程B |
- 在这个示例中,线程A持有资源1并尝试获取资源2,线程B持有资源2并尝试获取资源1,最终两个线程都被阻塞,陷入死锁。
11.
以下情况发生时,所需的最小线程数和最小共享资源数分别是多少?
-
有界优先级反转
:最小线程数为2(低优先级线程和高优先级线程),最小共享资源数为1。
-
无界优先级反转
:最小线程数为3(低、中、高优先级线程),最小共享资源数为1。
-
死锁
:最小线程数为2,最小共享资源数为2。
12.
假设多线程应用中发生了无界优先级反转,哪种解决方案对系统响应时间的负面影响最小?
- 优先级继承协议(PIP)对系统响应时间的负面影响最小。PIP仅在必要时提高低优先级线程的优先级,避免了不必要的优先级提升,从而减少了对其他线程执行的延迟。
13.
假设多线程应用中发生了无界优先级反转,哪种协议在关键部分开始时改变线程的优先级?
- 优先级天花板协议(PCP)在关键部分开始时改变线程的优先级。当线程获取共享资源时,PCP会立即将其优先级提高到优先级天花板。
14.
假设多线程应用中发生了无界优先级反转,哪种协议需要运行时内核的支持?
- 优先级继承协议(PIP)需要运行时内核的支持。PIP的实现完全在内核代码中,通过内核来检测和处理优先级继承。
15.
假设多线程应用中发生了死锁,哪种解决方案需要线程测试资源是否被锁定?
- 释放资源并重试的解决方案需要线程测试资源是否被锁定。当线程无法获取某个资源时,它需要释放所有已获取的资源并重新尝试获取,这就需要测试资源的锁定状态。
16.
哪种优先级分配策略可以计算响应时间截止日期是否能满足?
- 为截止日期最短的线程分配最高优先级的策略可以计算响应时间截止日期是否能满足。截止日期驱动调度优先调度截止日期最早的线程,有助于确保任务在截止日期前完成。
17.
考虑一个只有两个线程竞争单个共享资源的多线程应用。低优先级线程的关键部分执行时间为“L”秒,高优先级线程的关键部分执行时间为“H”秒。优先级反转的最大可能持续时间是多少?
- 优先级反转的最大可能持续时间为低优先级线程的关键部分执行时间“L”秒。因为在低优先级线程持有资源期间,高优先级线程会被阻塞。
18.
如果增加一个不使用任何共享资源的中等优先级线程,且不使用PCP或PIP,中等优先级线程每次运行不超过“M”秒。优先级反转的最大可能持续时间是多少?
- 优先级反转的最大可能持续时间为低优先级线程的关键部分执行时间“L”秒加上中等优先级线程的最大运行时间“M”秒,即 L + M。因为中等优先级线程可能会抢占低优先级线程,从而延长优先级反转的时间。
19.
假设使用以下协议,优先级反转的最大可能持续时间是多少?
-
优先级天花板协议(PCP)
:优先级反转的最大可能持续时间为低优先级线程的关键部分执行时间“L”秒。因为PCP会提高低优先级线程的优先级,避免中等优先级线程抢占。
-
优先级继承协议(PIP)
:优先级反转的最大可能持续时间为低优先级线程的关键部分执行时间“L”秒。因为PIP会在低优先级线程阻塞高优先级线程时提高其优先级。
6.2 案例分析
案例一:实时监控系统
某实时监控系统需要对多个传感器的数据进行实时采集和处理。系统中有三个线程:低优先级线程负责传感器数据的采集,中等优先级线程负责数据的初步处理,高优先级线程负责数据的分析和报警。三个线程共享一个数据缓冲区。
在初始情况下,低优先级线程获取了数据缓冲区并开始采集数据。此时,高优先级线程需要处理采集到的数据,但由于低优先级线程持有缓冲区,高优先级线程被阻塞。在低优先级线程采集数据的过程中,中等优先级线程准备好运行并抢占了低优先级线程。
如果使用固定优先级协议,高优先级线程需要等待低优先级线程完成采集(L)加上中等优先级线程完成初步处理(M)的时间,即 L + M。这会导致高优先级线程的响应时间大幅增加,可能无法满足实时监控的要求。
如果使用优先级天花板协议(PCP),当低优先级线程获取数据缓冲区时,其优先级会被提高到优先级天花板。这样,中等优先级线程无法抢占低优先级线程,高优先级线程只需要等待低优先级线程完成采集(L)的时间,提高了系统的实时性能。
如果使用优先级继承协议(PIP),当高优先级线程被阻塞时,内核会检测到低优先级线程的优先级低于高优先级线程,并将低优先级线程的优先级提高到与高优先级线程相同。这样,中等优先级线程无法抢占低优先级线程,高优先级线程也只需要等待低优先级线程完成采集(L)的时间,同样提高了系统的实时性能。
案例二:工业自动化系统
在工业自动化系统中,有多个任务需要协同工作,包括设备控制、数据传输和故障检测等。这些任务有不同的截止日期要求,例如设备控制任务需要在短时间内完成,以确保设备的正常运行;数据传输任务可以有稍长的截止日期。
使用截止日期驱动调度策略,系统会优先调度截止日期最早的任务。例如,如果设备控制任务的截止日期为50ms,数据传输任务的截止日期为100ms,系统会先执行设备控制任务,确保其在截止日期前完成。
同时,对于周期性任务,可以使用速率单调调度(RMA)。例如,某个设备的状态检测任务周期为20ms,另一个设备的参数调整任务周期为50ms。根据RMA,状态检测任务的优先级会更高,因为其执行周期更短。通过合理分配优先级,系统可以更好地满足各个任务的截止日期要求,提高工业自动化系统的稳定性和可靠性。
7. 总结与展望
7.1 总结
线程调度是多线程系统中的核心问题,它直接影响着系统的实时性能和稳定性。本文介绍了线程调度的基础概念,包括共享资源的仲裁、优先级继承协议(PIP)和优先级天花板协议(PCP)。PIP和PCP的目标是限制优先级反转的影响,确保高优先级线程能够及时获取资源。
在优先级分配方面,传统多用户系统采用最短任务优先策略,而实时系统则需要根据任务的截止日期和执行周期来分配优先级。截止日期驱动调度和速率单调调度是两种常用的实时调度策略,它们能够帮助系统更好地满足任务的时间要求。
死锁是线程调度中需要避免的问题,它会导致系统陷入阻塞状态。通过按相同顺序获取资源、释放资源并重试等策略,可以有效减少死锁的发生。看门狗定时器则是一种用于检测和恢复系统故障的机制,它能够在系统出现问题时及时重启,确保系统的连续运行。
7.2 展望
随着计算机技术的不断发展,线程调度面临着新的挑战和机遇。未来的线程调度技术可能会朝着以下几个方向发展:
1.
自适应调度
:系统能够根据实时负载和任务特性自动调整调度策略,以实现最优的性能。例如,在高负载情况下,系统可以动态调整任务的优先级,确保关键任务的执行。
2.
多核和分布式系统调度
:随着多核处理器和分布式系统的广泛应用,线程调度需要考虑多个处理器核心和节点之间的协同工作。如何在多核和分布式环境下实现高效的线程调度是一个重要的研究方向。
3.
人工智能辅助调度
:利用人工智能技术,如机器学习和深度学习,来优化线程调度策略。人工智能可以通过学习系统的历史数据和行为模式,预测任务的执行时间和资源需求,从而实现更智能的调度决策。
4.
安全和可靠性调度
:在安全关键系统中,线程调度需要确保系统的安全性和可靠性。未来的调度技术可能会更加注重任务的容错和恢复能力,以应对各种潜在的安全威胁和故障。
总之,线程调度是一个不断发展和演进的领域,需要我们持续关注和研究。通过合理选择和应用调度策略,我们可以提高多线程系统的性能和可靠性,满足不同应用场景的需求。
超级会员免费看
168万+

被折叠的 条评论
为什么被折叠?



