Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
相同优先级时间片轮转调度
关于时间片调度算法issue的分析与解决
10.00
发布于 2022-06-27 18:31:58 浏览:2834
订阅该版
[tocm] 在之前 [rt_schedule中need_insert_from_thread的问题](https://club.rt-thread.org/ask/question/67e5da9b4f0149e7.html) 提问中,笔者提出了当前时间片调度算法过于复杂,且高优先级一旦打断未执行完时间片的任务会导致该任务重新插入到其优先级readylist末尾,存在严重的不公平性(破坏了时间片的连续)。 当然笔者也PR了一个解决方案 https://github.com/RT-Thread/rt-thread/pull/5954 暂未合并 最近又有一个小伙伴发现了时间片调度的issue https://github.com/RT-Thread/rt-thread/issues/6092 大致的情况是: 1. 低优先级的存在任务A(ticks = a),B(ticks =b),; 高优先级任务C 2. 如果高优先级 C内存在延时c 正好等于A的时间片a 3. 结果就是低优先级的任务只有A在一直运行, B一直运行不了 这种情况的根本原因其实还是笔者之前提到的高优先级导致当前低优先级任务插入readylist位置不对的issue, 下面笔者再次配重新整理一下这个问题,配合图例逐步分析源码并结合测试例程展示不同情况下该issue导致的问题,并尝试解决。 # 源码分析 ## rt_tick_increase ``` /** * @brief This function will notify kernel there is one tick passed. * Normally, this function is invoked by clock ISR. */ void rt_tick_increase(void) { struct rt_thread *thread; rt_base_t level; RT_OBJECT_HOOK_CALL(rt_tick_hook, ()); level = rt_hw_interrupt_disable(); /* increase the global tick */ #ifdef RT_USING_SMP rt_cpu_self()->tick ++; #else ++ rt_tick; #endif /* RT_USING_SMP */ /* check time slice */ thread = rt_thread_self(); -- thread->remaining_tick; if (thread->remaining_tick == 0) { /* change to initialized tick */ thread->remaining_tick = thread->init_tick; thread->stat |= RT_THREAD_STAT_YIELD; rt_hw_interrupt_enable(level); rt_schedule(); } else { rt_hw_interrupt_enable(level); } /* check timer */ rt_timer_check(); } ``` 里面只做了两件事: 1. 当前任务的时间片递减, 如果用完了,置位RT_THREAD_STAT_YIELD状态,调用rt_schedule, 2. 检测是否有任务的超时了(等待资源或延时超时),如果超时,最终也会调用rt_schedule ## rt_schedule ### Who calling ![image-20220627091459627.png](https://oss-club.rt-thread.org/uploads/20220627/13ff8ea0da8862bade41c25da763f670.png.webp "image-20220627091459627.png") 排除componets中使用的情况,rt_schedule主要在下面情况中被使用 1. clock.c : 就是刚刚提及的在Systick中断中两种比较重要的调度: **时间片调度**和**超时调度** 2. ipc.c ,mempool.c: 另外一种比较重要的调度: 资源阻塞和就绪调度(**资源调度**) 3. scheduler.c, thread.c: 本身调度器和线程API的使用导致的直接**API调度** 4. timer.c : 软定时器超时调度,使用的也是_thread_timeout超时函数,也是**超时调度** 鉴于 **API调度**一般使用在初始化阶段,Application运行中主要使用的是**时间片调度,超时调度,资源调度** 。后面的讨论中主要绕后三种展开: ![image-20220627095239360.png](https://oss-club.rt-thread.org/uploads/20220627/ed3f79a1f6841b27ebc37cb431181a1c.png.webp "image-20220627095239360.png") ### 源码 ``` /** * @brief This function will perform scheduling once. It will select one thread * with the highest priority, and switch to it immediately. */ void rt_schedule(void) { rt_base_t level; struct rt_thread *to_thread; struct rt_thread *from_thread; /* disable interrupt */ level = rt_hw_interrupt_disable(); /* check the scheduler is enabled or not */ if (rt_scheduler_lock_nest == 0) { rt_ubase_t highest_ready_priority; if (rt_thread_ready_priority_group != 0) { /* need_insert_from_thread: need to insert from_thread to ready queue */ int need_insert_from_thread = 0; to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); if ((rt_current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) { if (rt_current_thread->current_priority < highest_ready_priority) { to_thread = rt_current_thread; } else if (rt_current_thread->current_priority == highest_ready_priority && (rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0) { to_thread = rt_current_thread; } else { need_insert_from_thread = 1; } rt_current_thread->stat &= ~RT_THREAD_STAT_YIELD_MASK; } if (to_thread != rt_current_thread) { /* if the destination thread is not the same as current thread */ rt_current_priority = (rt_uint8_t)highest_ready_priority; from_thread = rt_current_thread; rt_current_thread = to_thread; RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (from_thread, to_thread)); if (need_insert_from_thread) { rt_schedule_insert_thread(from_thread); } rt_schedule_remove_thread(to_thread); to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK); /* switch to new thread */ RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("[%d]switch to priority#%d " "thread:%.*s(sp:0x%08x), " "from thread:%.*s(sp: 0x%08x)\n", rt_interrupt_nest, highest_ready_priority, RT_NAME_MAX, to_thread->name, to_thread->sp, RT_NAME_MAX, from_thread->name, from_thread->sp)); #ifdef RT_USING_OVERFLOW_CHECK _rt_scheduler_stack_check(to_thread); #endif /* RT_USING_OVERFLOW_CHECK */ if (rt_interrupt_nest == 0) { extern void rt_thread_handle_sig(rt_bool_t clean_state); RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (from_thread)); rt_hw_context_switch((rt_ubase_t)&from_thread->sp, (rt_ubase_t)&to_thread->sp); /* enable interrupt */ rt_hw_interrupt_enable(level); #ifdef RT_USING_SIGNALS /* check stat of thread for signal */ level = rt_hw_interrupt_disable(); if (rt_current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING) { extern void rt_thread_handle_sig(rt_bool_t clean_state); rt_current_thread->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; rt_hw_interrupt_enable(level); /* check signal status */ rt_thread_handle_sig(RT_TRUE); } else { rt_hw_interrupt_enable(level); } #endif /* RT_USING_SIGNALS */ goto __exit; } else { RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n")); rt_hw_context_switch_interrupt((rt_ubase_t)&from_thread->sp, (rt_ubase_t)&to_thread->sp); } } else { rt_schedule_remove_thread(rt_current_thread); rt_current_thread->stat = RT_THREAD_RUNNING | (rt_current_thread->stat & ~RT_THREAD_STAT_MASK); } } } /* enable interrupt */ rt_hw_interrupt_enable(level); __exit: return; } ``` 调度的过程大致如下: 1. 关中断 2. 判断调度是否上锁,rt_thread_ready_priority_group是否为0 3. _scheduler_get_highest_priority_thread 获取当前最高优先级 4. 和当前优先级再次比较,确定真实的最高优先级 5. 获取最高优先级readylist的第一个任务 to_thread 6. to_thread 和 rt_current_thread 比较 7. 如果相等,简单设置状态,继续运行 8. 如果不相等,把当前任务插入其优先级readylist , 切换到 to_thread 9. 开中断 ### 得到to_thread的再判断 ![image-20220626122316431.png](https://oss-club.rt-thread.org/uploads/20220627/8e468dc6ce6a747bed51e19a17a40b13.png.webp "image-20220626122316431.png") 之所以搞的这么复杂,是因为当前的调度策略是把运行的任务,移出了readylist,那么获取的highest_ready_priority,to_thread只是红色圈中的,还需要结合正在运行的 thread 再次判断,下面将结合下图说明上图中的1,2,3 中情况 ![image-20220627104008931.png](https://oss-club.rt-thread.org/uploads/20220627/408b514a80ddf01e6577e9fa5fc55296.png.webp "image-20220627104008931.png") **假设 当前rt_thread_priority_table中存在三个优先级列表,下标分别为h, m, l (h
current_priority < highest_ready_priority) ``` 这个很好理解:没有和当前任务优先级相同的任务,其优先级readylist为空,存在如下两种情况: **1.1 低优先级就绪 ** 存在 B,D,E3个任务, 当前 E 因资源阻塞或者延时中,B正在运行;某一时刻任务E就绪(资源就绪或者超时),插入对应readylist后发起一次调度:highest_ready_priority = l > m ![image-20220627100541109.png](https://oss-club.rt-thread.org/uploads/20220627/607c75814e7bd14301770d22e780b432.png.webp "image-20220627100541109.png") > 对于某一任务的阻塞,图例仅展示资源阻塞(调度),或者延时阻塞(调度)中的一种,下同,不再说明 **1.2 当前运行任务的时间片用完 ** 同样存在 B,D,E 3个任务,当前B在运行; 某一时刻B的时间片用完,thread->stat |= RT_THREAD_STAT_YIELD,发起一次调度: highest_ready_priority = l > m 因为最高优先级m下,只有B一个任务,所以尽管其时间片已经使用完,还会复位ticks,再次运行。 ![image-20220627100541109.png](https://oss-club.rt-thread.org/uploads/20220627/607c75814e7bd14301770d22e780b432.png.webp "image-20220627100541109.png") > 这两种情况,最后运行的还是当前任务 #### 2. 当前优先级等于highest_ready_priority,且当前任务时间片未用完 ``` if (rt_current_thread->current_priority == highest_ready_priority && (rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0) ``` 存在 B,C 两个同优先级的任务,当前C因资源阻塞或者延时中,B正在运行; 某一时刻C就绪,插入对应readylist后发起一次调度: highest_ready_priority = m 但此时B的时间片还未用完,依旧无需礼让切换,让C等着吧。 ![image-20220627104748330.png](https://oss-club.rt-thread.org/uploads/20220627/20d21df97d2686267e12a74dc7bab33a.png.webp "image-20220627104748330.png") > 最后运行的也当前任务 #### 3 需要切换新的任务 可以直接列出剩余的三种情况, **3.1 当前优先级等于highest_ready_priority,且当前任务时间片用完** ``` if (rt_current_thread->current_priority == highest_ready_priority && (rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) != 0) ``` 存在 B,C 两个同优先级的任务,当前C已经resume就绪, B正在运行;某一时刻B的时间片用完,thread->stat |= RT_THREAD_STAT_YIELD,发起一次调度: highest_ready_priority = m ![image-20220627104801028.png](https://oss-club.rt-thread.org/uploads/20220627/20d21df97d2686267e12a74dc7bab33a.png.webp "image-20220627104801028.png") 需要礼让,把当前任务B插入对应优先级readylist后, 然后切换到C **3.2 当前优先级大于highest_ready_priority,且当前任务时间片用完** ``` if (rt_current_thread->current_priority > highest_ready_priority && (rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) != 0) ``` 这个情况实际是不存在的: (rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) != 0 表示时间片用完了,是通过rt_tick_increase->rt_schedule,即时间片用完,置位RT_THREAD_STAT_YIELD后发起的一次rt_schedule调度,调度结束会清除RT_THREAD_STAT_YIELD状态 rt_current_thread->current_priority > highest_ready_priority表示高优先级就绪,是通过 rt_tick_increase->rt_timer_check->_thread_timeout->rt_schedule 即高优先级的超时函数触发调度一次rt_schedule调度 很明显,这两种调度不会同时发生,就算rt_tick_increase->rt_schedule先发生,也会清除RT_THREAD_STAT_YIELD状态,导致条件不满足。 **3.3 当前优先级大于highest_ready_priority,且当前任务时间片未用完** ``` if (rt_current_thread->current_priority > highest_ready_priority && (rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0) ``` 存在任务 ABCDE, 当前A,E因资源阻塞或者延时中,B正在运行;某一时刻A就绪,其任务优先级高于当前任务,虽然当前任务时间片有剩余,也必须切出 ![image-20220627105736347.png](https://oss-club.rt-thread.org/uploads/20220627/1f7ac97e450f611085a08f489750be2e.png.webp "image-20220627105736347.png") 高优先级就绪后会把当前任务插入的readylist最后,也就是这个插入导致了众多的问题出现。 ![image-20220626121200429.png](https://oss-club.rt-thread.org/uploads/20220627/273af8da13e953920652f03ce9761fbb.png.webp "image-20220626121200429.png") ## rt_schedule_insert_thread ``` void rt_schedule_insert_thread(struct rt_thread *thread) { register rt_base_t temp; RT_ASSERT(thread != RT_NULL); /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* it's current thread, it should be RUNNING thread */ if (thread == rt_current_thread) { thread->stat = RT_THREAD_RUNNING | (thread->stat & ~RT_THREAD_STAT_MASK); goto __exit; } /* READY thread, insert to ready queue */ thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK); /* insert thread to ready list */ rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]), &(thread->tlist)); RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("insert thread[%.*s], the priority: %d\n", RT_NAME_MAX, thread->name, thread->current_priority)); /* set priority mask */ #if RT_THREAD_PRIORITY_MAX > 32 rt_thread_ready_table[thread->number] |= thread->high_mask; #endif /* RT_THREAD_PRIORITY_MAX > 32 */ rt_thread_ready_priority_group |= thread->number_mask; __exit: /* enable interrupt */ rt_hw_interrupt_enable(temp); } ``` 可以明显看出,rt_schedule_insert_thread调用的是rt_list_insert_before,即把当然运行任务插入到其优先级readylist的最后 # 问题 因为当前运行线程是remove出readylist的, 再插入readylist是必需的,但是使用rt_list_insert_before把未执行完时间片的任务插入到readylist的最后面,下次轮到该优先级时,会直接执行C,B的时间片被分成了两次或以上来执行,同理C也可能面临同样的情况。这就导致了很多问题。 ![image-20220627144433820.png](https://oss-club.rt-thread.org/uploads/20220627/136d2c18a7212980e1894032f662abde.png.webp "image-20220627144433820.png") ## 测试 测试环境: 基于stm32f4disc1开发板,创建3个线程分别控制LED3,LED4,LED5闪烁 1. rt_led1_thread: 优先级9,时间片1,延时 t1 = 5ms闪烁LED4 2. rt_led2_thread: 优先级11,时间片t2 =4(ms),自定义200延时闪烁LED5 3. rt_led3_thread: 优先级11,时间片t3 = 2(ms),自定义300延时闪烁LED3 ### 创建线程 ``` int main(void) { rt_thread_init( &rt_led1_thread, "LED1", led1_thread_entry, RT_NULL, &rt_led1_thread_stack[0], sizeof(rt_led1_thread_stack), 6, 1); rt_thread_startup(&rt_led1_thread); rt_thread_init( &rt_led2_thread, "LED2", led2_thread_entry, RT_NULL, &rt_led2_thread_stack[0], sizeof(rt_led2_thread_stack), 11, 4); rt_thread_startup(&rt_led2_thread); rt_thread_init( &rt_led3_thread, "LED3", led3_thread_entry, RT_NULL, &rt_led3_thread_stack[0], sizeof(rt_led3_thread_stack), 11, 2); rt_thread_startup(&rt_led3_thread); while (1) { rt_thread_mdelay(1000); } return RT_EOK; } ``` ### 线程入口函数 ``` /* customer short delay */ void delay (uint32_t count) { for(; count!=0; count--); } void led1_thread_entry( void *p_arg ) { for( ;; ) { HAL_GPIO_WritePin(GPIOD, LD4_Pin, GPIO_PIN_SET); rt_thread_delay( 5 ); HAL_GPIO_WritePin(GPIOD, LD4_Pin, GPIO_PIN_RESET); rt_thread_delay( 5); } } void led2_thread_entry( void *p_arg ) { for( ;; ) { HAL_GPIO_WritePin(GPIOD, LD5_Pin, GPIO_PIN_SET); delay( 300 ); HAL_GPIO_WritePin(GPIOD, LD5_Pin, GPIO_PIN_RESET); delay( 300 ); } } void led3_thread_entry( void *p_arg ) { for( ;; ) { HAL_GPIO_WritePin(GPIOD, LD3_Pin, GPIO_PIN_SET); delay( 300 ); HAL_GPIO_WritePin(GPIOD, LD3_Pin, GPIO_PIN_RESET); delay( 300 ); } } ``` ## 不同时间片对调度的影响 **1)t1 = 5, t2 = 4, t3=2** **新就绪的任务优先级高于当前任务,当前任务时间片有剩余** 通过逻辑分析仪抓取三个LED pin 电平 ![image-20220627152027184.png](https://oss-club.rt-thread.org/uploads/20220627/b6af85346d5e5c9237b221f2b651c5ef.png.webp "image-20220627152027184.png") 波形中可以明显看出thread2时间片 4ms(4 ticks) ,执行3ms后因为高优先级thread1打断,剩余的1ms会在thread3执行完后才执行。thread3甚至出现了连续执行的情况,稍后分析。总之时间片调度完全乱掉了。 **2)t1 = 5, t2 = 3, t3=2 ** **新就绪的任务优先级高于当前任务,当前任务时间片无剩余** ![image-20220627153223614.png](https://oss-club.rt-thread.org/uploads/20220627/65dd10f9f351f67925e38fa7fae55d12.png.webp "image-20220627153223614.png") 还记得我们上面提到了3.2 不能同时满足的条件吗? ``` if (rt_current_thread->current_priority > highest_ready_priority && (rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) != 0) ``` 只是因为软件的写法(第一次rt_scheduler后清除了RT_THREAD_STAT_YIELD状态),这个条件不能同时满足。从波形图看这两条件是可以同时满足的: 某一个tick中断了,线程thread2的时间片用完了,时间片调度度后, rt_timer_check发现正好有一个高优先级线程超时了,于是先后触发了两次调度: 第一次: 时间片调度, rt_current = thread3, rt_thread_priority_table[11] -> thread2 第二次:超时调度,把rt_current插入其readylist , rt_thread_priority_table[11] -> thread2-->thread3, rt_current = thread2 然后,当高优先级thread1再次阻塞时,首先运行的还是thread2。 这就是为什么thread2连续运行了两个t2(3*2),同理 thread3也是, 和我们期望的轮流执行不一致。 **3)t1 = 5, t2 = 5, t3=x (t2/t3 =5)** 高优先级的延时正好等于某一低优先级线程的时间片 ![image-20220627161546060.png](https://oss-club.rt-thread.org/uploads/20220627/a2472bf1459bac299fef1be3c75dbd79.png.webp "image-20220627161546060.png") 这种情况造成的结果比较明显,导致thread3一直没有运行的机会了,也就是小伙伴发现的issue https://github.com/RT-Thread/rt-thread/issues/6092 **从现象上看情况3是最极端,最严重的,但是你可以立即发现。 不像情况1,2虽然表面上每个thread还在执行,内部的时间片已经完全乱掉,往往这种隐形的issue会导致更加严重的后果。** # 解决办法 既然看到了时间片调度的众多issue,也知道了问题的原因:高优先级打断未执行完时间片的任务导致该任务被重新插入到其优先级readylist末尾 那么就开始着手解决。 ## 方案一 先看下小伙伴同时提出的解决办法:https://github.com/RT-Thread/rt-thread/pull/6095/files 他增加了一个RT_THREAD_STAT_SCHEDULING 状态来判断和避免issue ![image-20220627163838256.png](https://oss-club.rt-thread.org/uploads/20220627/db0821261606d7034e5769b518f70957.png.webp "image-20220627163838256.png") 这种做法应该没啥问题,暂未测试 ## 方案二 直击问题本质,既然是插入的问题导致的,调整一下插入顺序即可 ![image-20220627170547682.png](https://oss-club.rt-thread.org/uploads/20220627/41c355fb448e12f2f5a11a8ff6f76601.png.webp "image-20220627170547682.png") 除了时间片使用完,需要yield,插入其readylist的末尾,其他情况均插入readylist的头部 再次测试t1 = 5, t2 = 5, t3=2,结果如下: ![image-20220627165958080.png](https://oss-club.rt-thread.org/uploads/20220627/ca638c753cc4a2971af6d4565b67eff5.png.webp "image-20220627165958080.png") 可以看到,ticks连续执行了,线程没有重复执行,没有跳过,也没有不被执行的,完美解决。 ## 方案三 更近一步,可不可以不把运行的thread移除readylist呢?如果可以,没有remove,也就没有了insert,没有了issue 其实FreeRTOS使用的就是这种方案: **taskSELECT_HIGHEST_PRIORITY_TASK()** ``` #define taskSELECT_HIGHEST_PRIORITY_TASK() \ { \ UBaseType_t uxTopPriority; \ \ /* Find the highest priority list that contains ready tasks. */ \ portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \ configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \ listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ } /* taskSELECT_HIGHEST_PRIORITY_TASK() */ ``` **listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )** ``` #define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \ { \ List_t * const pxConstList = ( pxList ); \ /* Increment the index to the next item and return the item, ensuring */ \ /* we don't return the marker used at the end of the list. */ \ ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \ { \ ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ } \ ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \ } ``` 可以看出它再获取最高优先级后,会直接把readylist的index往后移动一次,获取下一个任务。虽然这个它的时间片ticks为常量1,但可以给我们提供一个很好的参考,没有remove, insert。 笔者之前PR的一个解决方案 https://github.com/RT-Thread/rt-thread/pull/5954 就是基于该方案 ### rt_list_jump_next 虽然rt_list_t也是一个双向链表,但是少了一个成员变量index,不能像FreeRTOS那样直接移动完成时间片的调度 首先我们需要新增一个rt_list_t操作函数rt_list_jump_next,完成rt_list_t头部往后移动一次,同时要保证list成员相对顺序不变 ``` /** * @brief move the list to its next's next position * * @param l list to insert it */ rt_inline void rt_list_jump_next(rt_list_t *l) { l->next->prev = l->prev; l->prev->next = l->next; l->prev = l->next; l->next->next->prev = l; l->next = l->next->next; l->prev->next = l; } ``` 大致操作如下: ![image-20220627182045409.png](https://oss-club.rt-thread.org/uploads/20220627/305ae2f95eb79672b4ef013bf2199fde.png.webp "image-20220627182045409.png") > 这种直接移动list head的做法,一次调度会有6次指针赋值操作, > > 而原来的一次调度remove(4次), insert(4次)加起来是8次指针赋值操作 ### 重定义_scheduler_get_highest_priority_thread ``` --- a/src/scheduler.c +++ b/src/scheduler.c @@ -180,6 +180,19 @@ static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *high highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1; #endif /* RT_THREAD_PRIORITY_MAX > 32 */ + /* if current thread is yield , move the head of priority list to next and change its status to READY */ + if((rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) != 0) + { + if(rt_current_thread->tlist.next != rt_current_thread->tlist.prev) + { + /* multiple threads, move the list head to next */ + rt_list_jump_next(&rt_thread_priority_table[rt_current_thread->current_priority]); + } + + /* clear YIELD and ready thread*/ + rt_current_thread->stat = RT_THREAD_READY | (rt_current_thread->stat & ~(RT_THREAD_STAT_YIELD_MASK|RT_THREAD_STAT_MASK)); + } + /* get highest ready priority thread */ highest_priority_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next, struct rt_thread, ``` ![image-20220627175149148.png](https://oss-club.rt-thread.org/uploads/20220627/04d946e87daa7483def3b46fe7f1ee7d.png.webp "image-20220627175149148.png") ### 简化rt_schedule 上面的分析可知,rt_schedule之所以搞的复杂,是因为获取的highest_ready_priority,to_thread不包含对当前正在运行thread的计算。现在我们不把rt_current_thread 移除其readylist,获得的highest_ready_priority,to_thread就是最终的 ```C @@ -258,7 +271,6 @@ void rt_system_scheduler_start(void) rt_current_thread = to_thread; #endif /* RT_USING_SMP */ - rt_schedule_remove_thread(to_thread); to_thread->stat = RT_THREAD_RUNNING; /* switch to new thread */ @@ -436,28 +448,8 @@ void rt_schedule(void) if (rt_thread_ready_priority_group != 0) { - /* need_insert_from_thread: need to insert from_thread to ready queue */ - int need_insert_from_thread = 0; - to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); - if ((rt_current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) - { - if (rt_current_thread->current_priority < highest_ready_priority) - { - to_thread = rt_current_thread; - } - else if (rt_current_thread->current_priority == highest_ready_priority && (rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0) - { - to_thread = rt_current_thread; - } - else - { - need_insert_from_thread = 1; - } - rt_current_thread->stat &= ~RT_THREAD_STAT_YIELD_MASK; - } - if (to_thread != rt_current_thread) { /* if the destination thread is not the same as current thread */ @@ -467,12 +459,6 @@ void rt_schedule(void) RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (from_thread, to_thread)); - if (need_insert_from_thread) - { - rt_schedule_insert_thread(from_thread); - } - - rt_schedule_remove_thread(to_thread); to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK); /* switch to new thread */ @@ -531,7 +517,6 @@ void rt_schedule(void) } else { - rt_schedule_remove_thread(rt_current_thread); rt_current_thread->stat = RT_THREAD_RUNNING | (rt_current_thread->stat & ~RT_THREAD_STAT_MASK); } } (END) ``` ### rt_system_scheduler_start ``` @@ -258,7 +271,6 @@ void rt_system_scheduler_start(void) rt_current_thread = to_thread; #endif /* RT_USING_SMP */ - rt_schedule_remove_thread(to_thread); to_thread->stat = RT_THREAD_RUNNING; /* switch to new thread */ ``` 同样测试t1 = 5, t2 = 5, t3=2,结果正常如下: ![image-20220627180659240.png](https://oss-club.rt-thread.org/uploads/20220627/a05b09d5595739809f8defa457ebdb38.png.webp "image-20220627180659240.png")
13
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
blta
这家伙很懒,什么也没写!
文章
12
回答
9
被采纳
2
关注TA
发私信
相关文章
1
关于时间片轮询中优先级高低导致轮询出错的问题
2
时间片轮转调度,,,,,,,,
3
时间片轮转调度,,,,,,
4
线程创建函数create最后的tick的作用?
5
跑timeslice_sample的示例,运行结果与文档说明的结果不一样
6
RT-Thread 时间片示例问题
7
时间片例程 结果和pdf给出的结果不同
8
新手问题:刚开始学,看到优先级和时间片,有个问题问一下
9
没有相同优先级线程时,线程时间片的问题
10
关于互斥量与线程时间片的问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
出出啊
1518
个答案
343
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
813
个答案
177
次被采纳
crystal266
549
个答案
161
次被采纳
whj467467222
1222
个答案
149
次被采纳
本月文章贡献
出出啊
1
篇文章
3
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部