4. 时间片轮转调度
任务目标
在ThreadX中,当多个线程具有相同优先级时,默认采用先到先得的调度方式,除非高优先级线程就绪,否则当前线程会一直运行。本案例通过配置时间片(Time Slice),使同优先级线程按时间片轮转方式交替执行,并观察调度效果。
实现原理
ThreadX通过线程创建函数tx_thread_create的time_slice参数为每个线程分配时间片。当同优先级线程的时间片耗尽时,调度器将CPU让给同优先级的下一个就绪线程。本案例创建两个同优先级线程,各自维护一个运行计数器,通过串口输出计数变化来验证轮转调度是否生效。
关键代码
#include "tx_api.h"
#include <stdio.h>
/* 定义线程控制块与计数器 */
TX_THREAD thread_0;
TX_THREAD thread_1;
volatile unsigned int counter_0 = 0;
volatile unsigned int counter_1 = 0;
/* 线程0入口函数 */
void thread_0_entry(ULONG id)
{
while(1)
{
counter_0++;
if(counter_0 % 100000 == 0)
{
printf("Thread 0: %u\r\n", counter_0);
}
}
}
/* 线程1入口函数 */
void thread_1_entry(ULONG id)
{
while(1)
{
counter_1++;
if(counter_1 % 100000 == 0)
{
printf("Thread 1: %u\r\n", counter_1);
}
}
}
/* 应用定义函数 */
void tx_application_define(void *first_unused_memory)
{
/* 创建两个同优先级线程,时间片各为4个tick */
tx_thread_create(&thread_0, "Thread 0", thread_0_entry, 0,
first_unused_memory, 1024,
15, 15, 4, TX_AUTO_START);
/* 第二个线程的栈紧随第一个线程之后 */
first_unused_memory = (void*)((char*)first_unused_memory + 1024);
tx_thread_create(&thread_1, "Thread 1", thread_1_entry, 0,
first_unused_memory, 1024,
15, 15, 4, TX_AUTO_START);
}
编译与运行
本案例基于ThreadX裸机环境,需包含ThreadX内核源码并链接tx.a库。编译命令示例:
arm-none-eabi-gcc -I./threadx/inc -c main.c -o main.o
arm-none-eabi-gcc main.o ./threadx/lib/tx.a -o demo.elf
将生成的demo.elf烧录至目标板,打开串口终端(波特率115200)观察输出。
验证方法
满足以下条件即可判定时间片轮转调度生效:
- 串口交替输出
Thread 0和Thread 1的计数信息,而非单个线程连续输出多次; - 两个计数器的增长速率大致相同,说明CPU时间被均分;
- 若将时间片参数改为0(禁用时间片),则只有一个线程持续运行,另一个线程永远得不到执行——可用此对比实验进一步确认。
预期输出示例
Thread 0: 100000
Thread 1: 100000
Thread 0: 200000
Thread 1: 200000
Thread 0: 300000
Thread 1: 300000
以上输出表明两个线程以时间片轮转方式交替获得CPU执行权。