RT-Thread入门(二) 线程的创建和管理

RT-Thread 新建进程 动/静态进程及线程的管理

        上一节我们已经建立了一个空白的RT-Thread工程,但这个工程只有一个进程main,今天我们在上次的基础上继续学习,通过创建多个进程感受RT-Thread和裸跑程序的不同。这次我们将将上次工程中控制LED和串口的两部分分离,独立成两个不同的线程。

简介

一、动态线程、静态线程区别

       使用静态定义方式时,必须先定义静态的线程控制块,并且定义好堆栈空间,然后调用rt_thread_init来完成线程的初始化工作。采用这种方式,线程控制块和堆栈占用的内存会放在RW段,这段空间在编译时就已经确定,它不是可以动态分配的,所以不能被释放。而只能使用rt_thread_detach函数将该线程控制块从对象管理器中脱离。
       使用动态定义方式rt_thread_create时,RT-Thread会动态申请线程控制块和堆栈空间。在编译时,编译器是不会感知到这段空间的,只有在程序运行时,RT-Thread才会从系统堆中申请分配这段内存空间,当不需要使用该线程时,调用rt_thread_delete函数就会将这段申请的内存空间重新释放到内存堆中。
       这两种方式各有利弊,静态定义方式会占用RW空间,但是不需要动态分配内存,运行时效率高。动态方式不会占用额外的RW空间,占用空间小,但是运行时需要动态分配内存,效率没有静态方式高。总的来说,这两种方式就是空间和时间效率的平衡,可以根据实际环境需求选择采用具体的分配方式。

二、相关函数介绍

1.首先呢当然是最重要的创建函数 rt_thread_create

rt_thread_t rt_thread_create(const char* name,
							void (*entry)(void* parameter),
							void* parameter,
							rt_uint32_t stack_size,
							rt_uint8_t priority,
							rt_uint32_t tick);

2.只要调用上面这个函数就可以使用动态方式创建一个进程,但这个线程处于初始状态,并未进入就绪线程的调度队列,我们可以在线程初始化创建成功后调用rt_thread_startup 让该线程进入就绪态:

rt_err_t rt_thread_startup(rt_thread_t thread);

3.当一些线程我们不再需要的时候我们需要删除他的时候可以调用 rt_thread_create()将他删除。

rt_err_t rt_thread_delete(rt_thread_t thread);

上面介绍的都是使用动态方法创建的线程,下面就是静态方法创建线程。

4.静态方式使用rt_thread_init()函数进行初始化。

rt_err_t rt_thread_init(struct rt_thread* thread,
						const char* name,
						void (*entry)(void* parameter), void* parameter,
						void* stack_start, rt_uint32_t stack_size,
						rt_uint8_t priority, rt_uint32_t tick);

将创建的线程启动和动态方法一样都是使用rt_thread_startup函数。

rt_err_t rt_thread_startup(rt_thread_t thread);

5.对于用使用静态方法创建的线程,使用rt_thread_detach()将线程删除。线程脱离函数如下:

rt_err_t rt_thread_detach (rt_thread_t thread);

6.上面就是创建、启动和删除一个线程的方法,除了这些函数之外RT-Thread系统还为我们提供了线程挂起和恢复函数:

rt_err_t rt_thread_suspend (rt_thread_t thread); //挂起线程  注意:应尽量避免使用该函数挂起自身线程
rt_err_t rt_thread_resume (rt_thread_t thread); //恢复一个挂起的线程  注意: 若回复的线程的优先级最高将会进行一次上下文切换。

7.当需要对线程进行一些其他控制时,例如动态更改线程的优先级,可以调用rt_thread_control函数接口

rt_err_t rt_thread_control(rt_thread_t thread, rt_uint8_t cmd, void* arg);

8.其他函数

RT-Thread还提供了一些其他功能函数,这里就不再详细介绍了,大家可以参考官方提供的编程指南进行学习。

rt_thread_t rt_thread_self(void);  //获得当前线程
rt_err_t rt_thread_yield(void);  //使线程让出处理器资源
rt_err_t rt_thread_sleep(rt_tick_t tick); //使线程睡眠 N 个时间片
rt_err_t rt_thread_delay(rt_tick_t tick);  //使线程睡眠 N 个时间片
rt_err_t rt_thread_mdelay(rt_int32_t ms);  //使线程睡眠 N 毫秒

三、编程练习

这里先说一下我们要实现的功能吧:
1.使用静态方式初始化一个线程:线程1,用于控制板子上的LED灯每隔500ms状态取反一次,并通过串口输出线程1 执行次数;
2.使用动态方式创建一个线程:线程2,用于控制串口1每隔500ms发送一次“Hello RT-Thread!”和线程2执行次数;
3.线程1每执行完将自身挂起,并在线程2中解挂。
4.线程2执行10次之后删除线程1。

程序代码如下:

#include 
#include "LED.h"

//LED0_thread 
#define LED0_priority 4
#define LED0_timeslices 5
static struct rt_thread led0_thread;//线程控制块
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t rt_led0_thread_stack[1024];//线程栈
  
//USART_thread 
#define USART_priority 5
#define USART_timeslices 5
#define USART_SIZE	1024

static rt_thread_t USART_thread = RT_NULL;

static void USART_enter(void *parameter);

//线程LED0
static void led0_thread_entry(void* parameter);
  
int main(void)
{
    // 创建静态线程
    rt_thread_init(&led0_thread,                 //线程控制块
                   "led0",                       //线程名字,在shell里面可以看到
                   led0_thread_entry,            //线程入口函数
                   RT_NULL,                      //线程入口函数参数
                   &rt_led0_thread_stack[0],     //线程栈起始地址
                   sizeof(rt_led0_thread_stack), //线程栈大小
                   LED0_priority,                            //线程的优先级
                   LED0_timeslices);                          //线程时间片
                               
    rt_thread_startup(&led0_thread);             //启动线程led0_thread,开启调度
		//创建USART线程
		USART_thread=rt_thread_create("USART_thread",
																	USART_enter,
																	RT_NULL,
																	USART_SIZE,
																	USART_priority,
																	USART_timeslices);
									 
	if(USART_thread != RT_NULL) rt_thread_startup(USART_thread);
    
}

//线程LED0
static void led0_thread_entry(void* parameter)
{
    rt_uint32_t count = 0;
	
    while (1)
    {
        LED0=~LED0;     
        rt_thread_mdelay(500);		
				rt_kprintf("thread led0 count: %d\r\n",  count ++); /* 打印线程计数值输出 */
				rt_kprintf("LED_thread 线程被挂起!\r\n");
				rt_thread_suspend(&led0_thread);
				rt_schedule();
    }
}

void USART_enter(void *parameter)
{
	  rt_uint32_t count_U = 0;
	
    while (1)
    {
				rt_thread_mdelay(500);	        
				rt_kprintf("thread usart count: %d\r\n",  count_U ++); /* 打印线程计数值输出 */
				rt_thread_resume(&led0_thread);		
				rt_kprintf("LED_thread 线程被解挂!\r\n");
				if(count_U==10)
				{
						rt_thread_detach(&led0_thread);
						rt_kprintf("LED_thread 线程被删除!\r\n");
				}
    }
}


运行结果如下:
RT-Thread入门(二) 线程的创建和管理_第1张图片

四、总结

       之前学过一些ucos,本打算在线程2中执行对线程1的挂起操作的,但后来发现调用rt_thread_mdelay(); 后这个线程就已经处于了挂起态,不能对他再次进行挂起操作了,所以改成了在线程1中进行自挂起。现在还不知道怎么实现相同的功能?有没有大神指点一下啊。

你可能感兴趣的:(RT-Thread,stm32)