【探讨】你真的了解 RTOS 的多线程吗?欢迎各位大佬来分析~

发布于 2019-12-09 17:47:19
    本帖最后由 aozima 于 2019-12-10 11:51 编辑


在 RT-Thread msh(优先级20) 运行 demo1 demo2 demo3 3条分别打印什么,为什么这样打印(先别运行哦,直接分析看你了解透彻没)?:lol:lol
先仅仅作者能看回帖,2天后揭晓大家的分析~

#include 

static void thread1_entry(void *parameter)
{
rt_thread_mdelay(100);
while (1)
{
rt_kprintf("1");
}
}

static void thread2_entry(void *parameter)
{
rt_thread_mdelay(500);
while (1)
{
rt_kprintf("2");
}
}

static void sample1_demo(void)
{
rt_thread_t tid1 = rt_thread_create("task1", thread1_entry, RT_NULL, 512, 25, 5);
if (tid1 != RT_NULL)
rt_thread_startup(tid1);

rt_thread_t tid2 = rt_thread_create("task2", thread2_entry, RT_NULL, 512, 10, 5);
if (tid2 != RT_NULL)
rt_thread_startup(tid2);
}
MSH_CMD_EXPORT(sample1_demo, sample1_demo);

static void sample2_demo(void)
{
rt_thread_t tid1 = rt_thread_create("task1", thread1_entry, RT_NULL, 512, 25, 5);
if (tid1 != RT_NULL)
rt_thread_startup(tid1);

rt_thread_t tid2 = rt_thread_create("task2", thread2_entry, RT_NULL, 512, 25, 5);
if (tid2 != RT_NULL)
rt_thread_startup(tid2);
}
MSH_CMD_EXPORT(sample2_demo, sample2_demo);

static void sample3_demo(void)
{
rt_thread_t tid1 = rt_thread_create("task1", thread1_entry, RT_NULL, 512, 10, 5);
if (tid1 != RT_NULL)
rt_thread_startup(tid1);

rt_thread_mdelay(600);

rt_thread_t tid2 = rt_thread_create("task2", thread2_entry, RT_NULL, 512, 10, 5);
if (tid2 != RT_NULL)
rt_thread_startup(tid2);
}
MSH_CMD_EXPORT(sample3_demo, sample3_demo);




查看更多

关注者
0
被浏览
931
24 个回答
liu2guang
liu2guang 认证专家 2019-12-09
yangjie 发表于 2019-12-9 18:13
这就是线程优先级抢占的例子嘛

由于shell线程优先级是20 ,所以楼主分了三种情况:

demo3改了下,少打一行,再试试~
liu2guang
liu2guang 认证专家 2019-12-09
yangjie 发表于 2019-12-9 18:13
这就是线程优先级抢占的例子嘛

由于shell线程优先级是20 ,所以楼主分了三种情况:


打印结果呢{:2_27:}
小木匠
小木匠 2019-12-09
demo1:
先打印400ms的1, 然后一直打印2
demo2:
先打印400ms的1, 然后打印5ms的2,再打印5ms的1,这样交替下去
demo3:
一直打印1

难道这么简单?
whj467467222
whj467467222 2019-12-09
分析一波这个题目:
DEMO1: 创建了两个任务 task 1 task2 他们的区别在于任务的优先级,以及start up的顺序。 start up只是把这给任务交给了调度器,但是这个时候调度还没有发生,调度器是在就绪列表中选择优先级最高的先执行,所以DEMO1应该是task2 先执行。

DEMO2:除了start up的顺序不一样其他一致,这样同优先级的任务先start up的任务先执行,剩下的轮转靠的是时间片。所以DEAM2 task1先执行

DEMO3:task1的优先级低,task2的任务优先级高,但是task1 start up之后delay了,这个时候调度器就介入了,那么task1已经执行了,但是task2还没有完成创建,所以DEMO3 是task1先执行。
lucifer
lucifer 2019-12-10
    本帖最后由 lucifer 于 2019-12-10 08:56 编辑


2........
1.....2........1........2.......
1...............2.......1.......2........
Liam
Liam 2019-12-10
偷偷运行了一下,发现果然分析错了,哈哈哈,一开始以为低优先级的死循环不能被高优先级的打断(只能主动让出处理器)。学到了○( ^皿^)っHiahiahia…
353896039
353896039 2019-12-10
一直打印2,因为优先级大于msh,并且while(1)里面没有delay
353896039
353896039 2019-12-10
应该先打印1 再一直打印2
123456
123456 2019-12-10
    本帖最后由 123456 于 2019-12-10 10:29 编辑


1. 2 1 2 1 2 1.。。。。。。
2. 111112111112111112。。。。。。
3. 111111211111121111112 。。。。。。

一句话:
1.优先级第一,即使优先级高的线程挂起,低优先级的线程也不会获取执行权。
2.优先级一样的情况下,采取时间片轮旋,顺序循环执行。
liu2guang
liu2guang 认证专家 2019-12-10
小木匠 发表于 2019-12-9 18:50
demo1:
先打印400ms的1, 然后一直打印2
demo2:


大佬来分析一波为什么这么打印~
liu2guang
liu2guang 认证专家 2019-12-10
heyuanjie87 发表于 2019-12-9 21:40
该帖仅网管可见


现在都可见了,杀猪哥不来一个分析吗:lol
liu2guang
liu2guang 认证专家 2019-12-10
whj467467222 发表于 2019-12-9 19:07
分析一波这个题目:
DEMO1: 创建了两个任务 task 1 task2 他们的区别在于任务的优先级,以及start up的顺 ...


打印结果呢?
liu2guang
liu2guang 认证专家 2019-12-10
123456 发表于 2019-12-10 10:28
1. 2 1 2 1 2 1.。。。。。。
2. 111112111112111112。。。。。。
3. ...


不对,不信你自己跑下代码看看
小木匠
小木匠 2019-12-13
不公布一下正确结果吗
liu2guang
liu2guang 认证专家 2019-12-13
小木匠 发表于 2019-12-13 08:56
不公布一下正确结果吗


马上
liu2guang
liu2guang 认证专家 2019-12-13
    本帖最后由 liu2guang 于 2019-12-13 10:53 编辑


1. 先说下现象:
demo1:先打印1,后只打印2
demo2:先打印1,后2/1交替
demo3:只打印1

demo1打印:
1.png

demo2打印:
2.png

demo3打印:
3.png

2. 代码分析

知识点:
1. 全抢占式调度原则,进入调度后永远选择
就绪列表(包含运行状态的线程)
中的最高优先级且拥有时间片的任务运行。
2. 延时会立刻挂起当前线程,进入调度。
3. 启动线程的时候会立刻调度。

demo1:
1. 我们在shell中运行sample1_demo命令,那么sample1_demo的运行环境就是在tshell线程中的,优先级为20。
2. 先调用rt_thread_create创建task1线程,优先级25并启动线程,这个时候会立刻进入调度,系统中有 idle tshell task1 3个线程,tshell是
就绪列表(包含运行状态的线程)中的最高优先级且拥有时间片的线程,所以满足调度条件继续运行tshell线程。
3. 那么继续运行tshell线程中的创建 sample2_demo 的代码。task2线程的优先级为10,启动task2线程会立刻调度,由于调度原则所以先运行 thread2_entry 线程。
4. 但是 task2 先运行了延时函数就把自己挂起了,所以这个时候又触发调度了,系统中有 idle tshell task1 task2 4个线程,tshell满足调度条件,那么又恢复到之前tshell被打断的地方继续运行。
5. sample2_demo函数退出,进入tshell等待命令挂起自己的状态,这个时候又触发调度,当前系统中还有 idel处于就绪状态,这个时候调度发现idle是最高,就运行idle线程
6. 等待100ms超时后,由ostick中断的到来触发调度,这个时候task1恢复就绪状态,那么当前系统中满足运行条件的线程为task1,task1开始死循环打印1
7. 再过400ms后,由ostick中断触发的调度中发现task2恢复就绪状态,那么当前系统中满足运行条件的线程为task2,系统就运行task2,由于task2是死循环,就算1个ostick来一次的调度也并不能切换任务,因为他用于处于就绪列表(包含运行状态的线程)中的最高优先级且拥有时间片的线程,所以最终先打印1,后一直打印2

demo2:
1. 我们在shell中运行sample2_demo命令,那么sample2_demo的运行环境就是在tshell线程中的,优先级为20。
2. 先调用rt_thread_create创建task1线程,优先级25并启动线程,这个时候会立刻进入调度,但是这个时候线程中有3个线程:task1,tshell,idel,由于tshell还是满足调度条件,所以先运行tshell线程中的创建sample2_demo的代码。
3. 接下来是创建task2线程优先级和task1一样是25并启动,又进入一次调度,但是这个tshell还是满足调度条件继续运行
4. sample2_demo函数退出,进入tshell等待命令挂起自己的状态,这个时候又触发调度,当前系统中还有 task1,task2,idel处于就绪状态,这个时候调度发现task1和2都是最高,那么由于task1先创建,那么先运行task1
5. task1函数中调用100ms延时挂起自己,又触发一次调度,当前系统中还有 task2,idel处于就绪状态,系统又调用task2
6. task2函数又调用500ms延时挂起自己,又触发一次调度,当前系统中还有 idel处于就绪状态,系统就调用idle线程
7. 等待100ms超时后,由ostick中断的到来触发调度,这个时候task1恢复就绪状态,那么当前系统中满足运行条件的线程为task1,task1开始死循环打印1,但是这一次由于task1和task2优先级一样,那么他们都会被调度器调度,这个时候时间片就上场了,task1和task2都是5个tick的时间片,什么叫时间片?我们之前说了1个ostick会触发一次中断,线程有一个计数值这个计数值就是维护这个线程连续运行了多少个tick,那么调度器发现你已经运行了5个tick那么你就需要交出cpu到同优先级的其他线程,那么5个tick后就会运行task2,就这种情况会反复运行task1 task2线程,这个时候反复打印1 2,所以最终:先打印1,后2/1交替

demo3
1. 我们先在shell中运行sample3_demo命令,那么sample3_demo的运行环境就是在tshell线程中的,优先级为20。
2. 首先创建10优先级的task1线程,启动task1线程触发调度,task1满足运行条件进入task1运行
3. 运行task1线程的时候,首先延时了100ms,所以task1挂起,当前系统中 就绪的线程只有tshell idle,那么又调度回tshell运行
4. tshell这个时候调用600ms的延时,tshell被挂起了,所以调度器只能执行idle线程了,
5. 100ms过去后,task1恢复就绪状态并进入运行状态,由于task1是死循环,就算1个ostick来一次的调度也并不能切换任务,因为他用于处于就绪列表(包含运行状态的线程)中的最高优先级且拥有时间片的线程,那么task2永远不会被创建,那么也就是只会打印1,不会打印2

whj467467222
whj467467222 2019-12-13
liu2guang 发表于 2019-12-13 09:56
1. 先说下现象:
demo1:先打印1,后只打印2
demo2:先打印1,后2/1交替


:)
123456
123456 2019-12-13
楼主,如果几个延时的操作,全部换成rt_thread_yield,结果又是如何呢?
liu2guang
liu2guang 认证专家 2019-12-13
123456 发表于 2019-12-13 10:52
楼主,如果几个延时的操作,全部换成rt_thread_yield,结果又是如何呢?


可以参考下我上面的分析,不过yield不建议使用这个api
123456
123456 2019-12-13
liu2guang 发表于 2019-12-13 10:53
可以参考下我上面的分析,不过yield不建议使用这个api


yield 应该采用那个API,指导一下。
liu2guang
liu2guang 认证专家 2019-12-13
123456 发表于 2019-12-13 10:57
yield 应该采用那个API,指导一下。


线程有关利用 rt_thread_mdelay/delay/sleep 或者 IPC 相关api来交出 cpu 的执行权限,调度器最好不要手动挂起,例如yield,挂起线程,恢复线程,这样你系统调度可能就不合理,当然IPC还有延时其实就是用yield,挂起线程,恢复线程来实现的,所以说你能驾驭得住就可以用,但是99%的人驾驭不住的
快去买药
快去买药 2019-12-13
楼主,一个线程延时的时候,该线程的时间片还会计数吗或者说消耗它的时间片吗,这里应该看哪里的代码分析啊

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友