RT-Thread互斥量原理说明及使用示例

1、互斥量原理

互斥量也叫互斥锁,和信号量的原理类似,但是不同于优先级和抢断机制。想了一个互斥量原理的例子感觉挺有道理的,这时候你去卫生间,进去后需要把门从里面关上(不关的不算哈- -),你就持有了当前卫生间的所有权,别人都不能再进来,这时候你就拥有了互斥量。当你解决完出来后把门打开,这时候你就把互斥量释放了。这个过程就是获取和释放互斥量。
互斥量在你拥有的过程不会被中断机制影响,信号量有可能会造成优先级反转,而互斥量不会。回到刚才的例子,当你进入卫生间这时候,有老板来敲了一下门(老板指优先级更高的线程),问:“有人吗“?你说:“(老头音)有人,稍等一下”,这时候老板就在门外等着了,忽然主管(比老板优先级低,比你优先级高的线程)敲了一下门,问:“有人吗?”,你说:“(老头音)有人,刚刚老板敲过门了,你也稍等一下吧。”,这时候主管就会排在老板的后面等。等你打开门后,下个进入卫生间的人就会是老板。

2、官方文档函数说明

互斥量控制块的数据结构
struct rt_mutex
{
struct rt_ipc_object parent; /* 继承自ipc_object类 /
rt_uint16_t value; /
互斥量的值 /
rt_uint8_t original_priority; /
持有线程的原始优先级 /
rt_uint8_t hold; /
持有线程的持有次数 */
struct rt_thread owner; / 当前拥有互斥量的线程 /
};
/
rt_mutext_t为指向互斥量结构体的指针 /
typedef struct rt_mutex
rt_mutex_t;
rt_mutex对象从rt_ipc_object中派生,由IPC容器管理。

创建互斥量
创建一个互斥量时,内核首先创建一个互斥量控制块,然后完成对该控制块的初始化工作。
rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag);
可以调用rt_mutex_create函数创建一个互斥量,它的名字有name所指定。创建的互斥量由于指定的flag不同,而有不同的意义: 使用PRIO优先级flag创建的IPC对象,在多个线程等待资源时,将由优先级高的线程优先获得资源。而使用FIFO先进先出flag创建的IPC对象,在多个线程等待资源时,将按照先来先得的顺序获得资源。
参数 说明
name 自己指定的信号量名字
flag 可以采取的运行方式,可以选取如下参数
#define RT_IPC_FLAG_FIFO 0x00 /* IPC参数采用FIFO先进先出方式*/
#define RT_IPC_FLAG_PRIO 0x01 /* IPC参数采用优先级方式*/
函数返回
创建成功返回指向互斥量的互斥量句柄;否则返回RT_NULL。
删除互斥量
系统不再使用互斥量时,通过删除互斥量以释放系统资源。删除互斥量使用下面的函数
接口:
rt_err_t rt_mutex_delete (rt_mutex_t mutex);
当删除一个互斥量时,所有等待此互斥量的线程都将被唤醒,等待线程获得的返回值是-RT_ERROR。然后系统将该互斥量从内核对象管理器链表中删除并释放互斥量占用的内存空间。
参数 说明
mutex 要处理的互斥量句柄
函数返回
RT_EOK
初始化互斥量
静态互斥量对象的内存是在系统编译时由编译器分配的,一般放于数据段或ZI段中。在使用这类静态互斥量对象前,需要先进行初始化。初始化互斥量使用下面的函数接口:
rt_err_t rt_mutex_init (rt_mutex_t mutex, const char* name, rt_uint8_t flag);
使用该函数接口时,需指定互斥量对象的句柄(即指向互斥量控制块的指针),互斥量名称以及互斥量标志。互斥量标志可用上面创建互斥量函数里提到的标志。
参数 说明
mutex 先定义好的的互斥量句柄
name 自己指定的信号量名字
flag 可以采取的运行方式,可以选取如下参数
#define RT_IPC_FLAG_FIFO 0x00 /* IPC参数采用FIFO先进先出方式*/
#define RT_IPC_FLAG_PRIO 0x01 /* IPC参数采用优先级方式*/
函数返回
RT_EOK
脱离互斥量
脱离互斥量将把互斥量对象从内核对象管理器中删除。脱离互斥量使用下面的函数接口:
rt_err_t rt_mutex_detach (rt_mutex_t mutex);
使用该函数接口后,内核先唤醒所有挂在该互斥量上的线程(线程的返回值是RT_ERROR),然后系统将该互斥量从内核对象管理器链表中删除。
参数 说明
mutex 互斥量句柄
获取互斥量
线程通过互斥量申请服务获取互斥量的所有权。线程对互斥量的所有权是独占的,某一
个时刻一个互斥量只能被一个线程持有。获取互斥量使用下面的函数接口:
rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time);
如果互斥量没有被其他线程控制,那么申请该互斥量的线程将成功获得该互斥量。如果互斥量已经被当前线程线程控制,则该互斥量的持有计数加1,当前线程也不会挂起等待。如果互斥量已经被其他线程占有,则当前线程在该互斥量上挂起等待,直到其他线程释放它或者等待时间超过指定的超时时间。
参数 说明
mutex 想要获取的互斥量对象的句柄
time 想要等待的时间
函数返回
成功获得互斥量返回RT_EOK;超时返回-RT_ETIMEOUT;其他返回-RT_ERROR。
释放互斥量
当线程完成互斥资源的访问后,应尽快释放它占据的互斥量,使得其他线程能及时获取该互斥量。释放互斥量使用下面的函数接口:
rt_err_t rt_mutex_release(rt_mutex_t mutex);
使用该函数接口时,只有已经拥有互斥量控制权的线程才能释放它,每释放一次该互斥量,它的持有计数就减1。当该互斥量的持有计数为零时(即持有线程已经释放所有的持有操作),它变为可用,等待在该信号量上的线程将被唤醒。如果线程的运行优先级被互斥量提升,那么当互斥量被释放后,线程恢复为持有互斥量前的优先级。
参数 说明
mutex 指定的互斥量句柄
函数返回
RT_EOK

3、代码示例

使用互斥量的例程如下例所示:

/*
* 程序清单:互斥量使用例程
*
* 这个例子将创建3个动态线程以检查持有互斥量时,持有的线程优先级 * 是否被调整到等待线程优先级中的最高优先级。
*
* 线程1,2,3的优先级从高到低分别被创建,
* 线程3先持有互斥量,而后线程2试图持有互斥量,此时线程3的优先级	* 应该被提升为和线程2的优先级相同。线程1用于检查线程3的优先级是 * 否被提升为与线程2的优先级相同。
*/
#include <rtthread.h>
#include "tc_comm.h"
/* 指向线程控制块的指针 */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
static rt_thread_t tid3 = RT_NULL;
static rt_mutex_t mutex = RT_NULL;
/* 线程1入口 */
static void thread1_entry(void* parameter)
{
	/* 先让低优先级线程运行 */
	rt_thread_delay(10);
	/* 此时thread3持有mutex,并且thread2等待持有mutex */
	/* 检查thread2与thread3的优先级情况 */
	if (tid2->current_priority != tid3->current_priority)
	{
		/* 优先级不相同,测试失败 */
		tc_stat(TC_STAT_END | TC_STAT_FAILED);
		return;
	}
}
/* 线程2入口 */
static void thread2_entry(void* parameter)
{
	rt_err_t result;
	/* 先让低优先级线程运行 */
	rt_thread_delay(5);
	while (1)
	{
		/*
		* 试图持有互斥锁,此时thread3持有,应把thread3的优先级提升
		* 到thread2相同的优先级
		*/
		result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
		if (result == RT_EOK)
		{
			/* 释放互斥锁 */
			rt_mutex_release(mutex);
		}
	}
}
/* 线程3入口 */
static void thread3_entry(void* parameter)
{
	rt_tick_t tick;
	rt_err_t result;
	while (1)
	{
		result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
		result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
		if (result != RT_EOK)
		{
			tc_stat(TC_STAT_END | TC_STAT_FAILED);
		}
		/* 做一个长时间的循环,总共50个OS Tick */
		tick = rt_tick_get();
		while (rt_tick_get() - tick < 50) ;
		rt_mutex_release(mutex);
		rt_mutex_release(mutex);
	}
}
int mutex_simple_init()
{
	/* 创建互斥锁 */
	mutex = rt_mutex_create("mutex", RT_IPC_FLAG_FIFO);
	if (mutex == RT_NULL)
	{
		tc_stat(TC_STAT_END | TC_STAT_FAILED);
		return 0;
	}
	/* 创建线程1 */
	tid1 = rt_thread_create("t1",
	thread1_entry, /* 线程入口是thread1_entry */
	RT_NULL, /* 入口参数是RT_NULL */
	THREAD_STACK_SIZE, THREAD_PRIORITY - 1, THREAD_TIMESLICE);
	if (tid1 != RT_NULL)
		rt_thread_startup(tid1);
	else
		tc_stat(TC_STAT_END | TC_STAT_FAILED);
	/* 创建线程2 */
	tid2 = rt_thread_create("t2",
	thread2_entry, /* 线程入口是thread2_entry */
	RT_NULL, /* 入口参数是RT_NULL */
	THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
	if (tid2 != RT_NULL)
		rt_thread_startup(tid2);
	else
		tc_stat(TC_STAT_END | TC_STAT_FAILED);
	/* 创建线程3 */
	tid3 = rt_thread_create("t3",
	thread3_entry, /* 线程入口是thread3_entry */
	RT_NULL, /* 入口参数是RT_NULL */
	THREAD_STACK_SIZE, THREAD_PRIORITY + 1, THREAD_TIMESLICE);
	if (tid3 != RT_NULL)
		rt_thread_startup(tid3);
	else
		tc_stat(TC_STAT_END | TC_STAT_FAILED);
	return 0;
}
#ifdef RT_USING_TC
static void _tc_cleanup()
{
	/* 调度器上锁,上锁后,将不再切换到其他线程,仅响应中断 */
	rt_enter_critical();
	/* 删除线程 */
	if (tid1 != RT_NULL && tid1->stat != RT_THREAD_CLOSE)
		rt_thread_delete(tid1);
	if (tid2 != RT_NULL && tid2->stat != RT_THREAD_CLOSE)
		rt_thread_delete(tid2);
	if (tid3 != RT_NULL && tid3->stat != RT_THREAD_CLOSE)
		rt_thread_delete(tid3);
	if (mutex != RT_NULL)
	{
		rt_mutex_delete(mutex);
	}
	/* 调度器解锁 */
	rt_exit_critical();
	/* 设置TestCase状态 */
	tc_done(TC_STAT_PASSED);
	}
		int _tc_mutex_simple()
	{
	/* 设置TestCase清理回调函数 */
	tc_cleanup(_tc_cleanup);
	mutex_simple_init();
	/* 返回TestCase运行的最长时间 */
	return 100;
}
/* 输出函数命令到finsh shell中 */
FINSH_FUNCTION_EXPORT(_tc_mutex_simple, sime mutex example);
#else
/* 用户应用入口 */
int rt_application_init()
{
	mutex_simple_init();
	return 0;
}
#endif

猜你喜欢

转载自blog.csdn.net/weixin_42560250/article/details/105646335