B7课程:互斥锁
上一课B6讲的是临界区。临界区的做法比较硬:把调度器锁住,这段时间别的任务插不进来。能解决问题,但代价也明显——锁得时间一长,实时性就差了。
B7换了个思路:任务该阻塞就阻塞,别把整个系统冻住。这就是互斥锁(mutex)。多任务共用一个资源(本例是串口)时,如何保证同一时刻只有一个任务在用。
(图一)
工程跑在APM32(Cortex-M0)上,应用层主要看al_main_task.c。
main里关掉中断,初始化OS,创建一个优先级为1的main_task,然后os_start()。真正干活的逻辑都在al_main_task里:
配SysTick(1ms)、USART1、LED
os_mutex_init(&g_mutex)
再创建两个优先级都是2的任务:task1、task2:
void al_task1(void *arg)
{
while (1)
{
os_mutex_lock(&g_mutex, OS_PEND_FOREVER);
printf("11111\n");
al_busy_delay(5000*1000);
os_mutex_unlock(&g_mutex);
}
}
void al_task2(void *arg)
{
os_tick_delay(100);
while (1)
{
os_mutex_lock(&g_mutex, OS_PEND_FOREVER);
printf("22222\n");
al_busy_delay(5000*1000);
os_mutex_unlock(&g_mutex);
}
}
共享资源就是串口。printf底层走fputc,一个字符一个字符往USART塞。如果两个任务同时打字,串口输出很容易搅在一起。锁把「打印+ busy delay」包起来,谁拿到锁谁独占这段输出。
task2开头故意os_tick_delay(100),让task1先跑一会儿,现象更直观。两个任务优先级相同,中间那段al_busy_delay是空转,不会主动让出CPU;时间片到了还是可能切走,但因为锁还在手里,另一个任务进不了临界代码,最多在外面干等。
串口里正常应该看到完整的一行行11111 / 22222,不会出现11212...这种穿插。
修改完后,按预期的打印。后期有兴趣可以研究下os_tick.c底层的机制,就会更清晰地了解到为什么会是这个结果了。
(图二)
建议自己改一版对比
把两个任务里的os_mutex_lock/unlock注释掉,重新烧录看串口。大概率输出会糊成一团。再加回锁,现象立刻干净。这一步比只看代码印象深。
也可以试着把al_busy_delay挪到unlock后面,观察两个任务输出切换会不会更密——这能帮你体会「锁住的时间」到底意味着什么。
(图三)