题27
题目
[!question]+
【2023 统考真题】现要求学生使用 swap 指令和布尔型变量 lock 实现临界区互斥。lock 为线程间共享的变量, 当 lock 的值为 TRUE 时线程不能进入临界区, 为 FALSE 时线程能够进入临界区。某同学编写的实现临界区互斥的伪代码如下图所示。

某同学编写的伪代码
bool lock = FALSE; // 共享变量
...
bool key = TRUE; // 进入区
if (key == TRUE)
swap(key, lock); // 交换key和lock的值
临界区:
// 临界区代码
lock = TRUE; // 退出区
...newSwap()的代码
void newSwap(bool &a, bool &b) {
bool temp = *a;
*a = *b;
*b = temp;
}请回答下列问题。
- 图(a)的伪代码中哪些语句存在错误? 将其改为正确的语句 (不增加语句的条数)。
- 图(b)给出了交换两个变量值的函数
newSwap()的代码, 是否可以用函数调用语句 “newSwap(&key, &lock)” 代替指令 “swap key, lock” 以实现临界区互斥? 为什么?
分析
[!NOTE]+
原始伪代码:
bool lock = FALSE; // 共享变量
...
void T0() {
bool key = TRUE;
if (key == TRUE)
swap key, lock; // 交换key和lock的值
临界区;
lock = TRUE;
...
}
void T1() {
bool key = TRUE;
if (key == TRUE)
swap key, lock; // 交换key和lock的值
临界区;
lock = TRUE;
...
}初始状态:
lock = FALSE(表示临界区当前未被占用)
线程 T0 和 T1 同时尝试进入临界区:
- T0 执行:
bool key = TRUE;(T0 的局部变量key被初始化为TRUE)if (key == TRUE)(条件成立,执行下一步)swap key, lock;(交换key和lock的值)- 执行前:
key = TRUE,lock = FALSE - 执行后:
key = FALSE,lock = TRUE
- 执行前:
临界区;(T0 进入临界区)
- 此时 T1 开始执行,与 T0 并发:
bool key = TRUE;(T1 的局部变量key被初始化为TRUE)if (key == TRUE)(条件成立,执行下一步)swap key, lock;(交换key和lock的值)- 执行前:
key = TRUE,lock = TRUE - 执行后:
key = TRUE,lock = TRUE
- 执行前:
临界区;(T1 进入临界区)
- T0 执行:
lock = TRUE;(T0 退出临界区,但lock的值已经是TRUE,没有起到解锁的作用)
- T1 执行:
lock = TRUE;(T1 退出临界区,但lock的值已经是TRUE,没有起到解锁的作用)
分析结果:
- T0 先执行: T0 进入临界区后,
lock被设置为TRUE。 - T1 并发执行: T1 在 T0 还在临界区内时执行,并交换了key 和 lock 的值,此时
lock的值已经为 true,key的值变为 true,依然能进入临界区。 - 互斥失效: T0 和 T1 都进入了临界区,没有实现互斥访问。
- 未解锁: 两个线程执行结束时,
lock的值最终都是TRUE,没有进行解锁操作,导致后续线程无法进入临界区。
关键错误点:
if条件判断: 使用if (key == TRUE)只能执行一次swap操作,无法保证线程在lock为TRUE时等待。- 退出区错误: 使用
lock = TRUE退出临界区是错误的,应该将lock的值设置为FALSE以释放锁。
总结:
原始伪代码的问题在于:
- 没有实现等待: 当
lock为TRUE时,其他线程应该等待,但if语句无法实现这个功能。 - 退出区错误: 没有正确的解锁操作,导致其他线程永远无法进入临界区。
为了实现互斥访问,需要使用循环等待(while)和正确的解锁操作(lock = FALSE或swap key, lock)。

模拟两个线程同时运行这个代码,就知道是否互斥了,不能只想一个进程在跑,因为这里又公共变量
解
[!done]+
- if 语句无法实现对临界区的互斥访问, 因为 if 语句执行后, 不论结果如何, 线程都能访问临界区。本题使用 swap 指令和 lock 变量来实现对临界区的互斥访问, 当线程不能进入临界区时, 本身并不会主动放弃 CPU, 因此需要要让线程在进入区中循环检查 lock 值, 可以使用 while 循环, 当 lock 值为 TRUE 时, 线程一直执行 while 循环的内容, 直到 lock 值被修改为 FALSE 时, 线程才能进入临界区, 因此将进入区中的语句 “if (key = TRUE) swap key, lock” 修改为 “while (key = TRUE) swap key, lock”。在退出区中,代表该线程对临界资源的访问已经结束, 此时需要将 lock 值设为 FALSE, 代表其他线程可以访问临界区, 因此将退出区中的语句 “lock = TRUE” 修改为 “lock = FALSE”。
- 否。因为多个线程可以并发执行
newSwap(),newSwap()执行时传递给形参b的是共享变量lock的地址, 在newSwap()中对lock既有读操作又有写操作, 并发执行时不能保证实现两个变量值的原子交换,从而导致并发执行的线程同时进入临界区。例如,线程A和线程B并发执行,初始时lock值为FALSE,当线程A执行完*a = *b后发生了进程调度,切换到线程B执行, 线程B执行完newSwap后发生线程切换, 此时线程A和B都能进入临界区, 不能实现互斥访问。