信号量实践

信号量:信号量是线程同步的手段之一。信号量的值表示可用资源数量,线程获取一次信号量,信号量的值就会减1,当信号量的值减到0后,再有线程获取信号量时,该线程就会被挂起到信号量的等待队列中,等待其他线程释放信号量。

信号量官方的说明是:信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。

信号量的经典例子:

 一个典型的应用场合就是停车位模型,总共有多少个车位,就是多少个信号量,入口进入一辆车信号量-1,出口离开一辆车信号量+1。

又比如 两个线程之间的同步,信号量的值初始化成 0,而尝试获得该信号量的线程,一定需要等待另一个释放信号量的线程先执行完。

信号量记住一句话基本就可以,释放一次信号量就+1,获取一次就-1,如果信号量数据为0,那么尝试获取的线程就会挂机,直到有线程释放信号量使得信号量大于0

信号量操作:

1、创建信号量

/*
参数的含义:
1、name 	信号量名称
2、value 	信号量初始值
3、flag 	信号量标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回值:
信号量创建成功,返回信号量的控制块指针
信号量创建失败,返回RT_BULL 
*/
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag)

对于最后的参数 flag,决定了当信号量不可用时(就是当信号量为0的时候),多个线程等待的排队方式。只有RT_IPC_FLAG_FIFO (先进先出)或 RT_IPC_FLAG_PRIO(优先级等待)两种 flag。

2、删除信号量

/*
参数:
sem 	rt_sem_create() 创建的信号量对象,信号量句柄
返回值:
RT_EOK 	删除成功
*/
rt_err_t rt_sem_delete(rt_sem_t sem)

3、初始化信号量

/**
参数的含义:
1、sem 		信号量对象的句柄,就是开始定义的信号量结构体变量
2、name 	信号量名称
3、value 	信号量初始值
4、flag 	信号量标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回值:
RT_EOK 	初始化成功
 */
rt_err_t rt_sem_init(rt_sem_t    sem,
                     const char *name,
                     rt_uint32_t value,
                     rt_uint8_t  flag)

4、脱离信号量

/*
参数:
sem 	信号量对象的句柄
返回值:
RT_EOK 	脱离成功
*/
rt_err_t rt_sem_detach(rt_sem_t sem);

 5、获取信号量

当信号量值大于零时,线程将获得信号量,并且相应的信号量值会减 1

注意!要等待的时间是系统时钟节拍(OS Tick)。

/**
参数:
1、sem 		信号量对象的句柄
2、time 	指定的等待时间,单位是操作系统时钟节拍(OS Tick)
返回值:
RT_EOK 			成功获得信号量
-RT_ETIMEOUT 	超时依然未获得信号量
-RT_ERROR 		其他错误
 */
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time)

6、 无等待获取信号量

//就是上面获取的等待时间为0的方式
rt_err_t rt_sem_trytake(rt_sem_t sem)
{
    return rt_sem_take(sem, 0);
}

7、释放信号量

释放信号量可以使得该信号量+1,如果有线程在等待这个信号量,可以唤醒这个线程。

/**
参数:
sem 	信号量对象的句柄
返回值:
RT_EOK 	成功释放信号量
 */
rt_err_t rt_sem_release(rt_sem_t sem)

 停车场示例:

#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

static rt_thread_t key2_thread = RT_NULL; //
static rt_thread_t key3_thread = RT_NULL; //

rt_sem_t mysem;

rt_int8_t flag1=0;
rt_int8_t flag2=0;
rt_int8_t key2_read=1;
rt_int8_t key3_read=1;


static void key2_thread_entry(void *par)
{
		static rt_err_t result;
		while(1)
		{
				if(key2_read == 0)
				{
						result = rt_sem_take(mysem, 1000);
						if (result != RT_EOK)
						{
								rt_kprintf("the is no parking spaces now...\r\n");
						}
						else
						{
								rt_kprintf("one car get in!,we have %d parking spaces now...\r\n",mysem->value);
						}
						key2_read = 1;
				}
				rt_thread_mdelay(1);
		} 
}
static void key3_thread_entry(void *par)
{
		while(1)
		{
				if(key3_read == 0)
				{
						if(mysem->value < 10)
						{
								rt_sem_release(mysem);
								rt_kprintf("one car get out!,we have %d parking spaces now...\r\n",mysem->value);
						}
						key3_read = 1;
				}
				rt_thread_mdelay(1);
		} 
}
int main(void)
{

		mysem = rt_sem_create("my_sem1", 10, RT_IPC_FLAG_FIFO);
		if(RT_NULL == mysem)
		{
				LOG_E("create sem failed!...\n");
		}
		else 
				LOG_D("we have 10 parking spaces now...\n");
		key2_thread = rt_thread_create("key2_control",
																		key2_thread_entry,
																		RT_NULL,
																		512,
																		RT_THREAD_PRIORITY_MAX -2,
																		50);
		/* 如果获得线程控制块,启动这个线程 */
		if (key2_thread != RT_NULL)
				rt_thread_startup(key2_thread);
		key3_thread = rt_thread_create("key3_control",
																		key3_thread_entry,
																		RT_NULL,
																		512,
																		RT_THREAD_PRIORITY_MAX -2,
																		50);
		/* 如果获得线程控制块,启动这个线程 */
		if (key3_thread != RT_NULL)
				rt_thread_startup(key3_thread);
		return RT_EOK;
}

调试界面:

 把key_read2和key_read3通过右键Add to watch1,调试界面如下:

 key_read2为1表示未按下,为0表示按下,每按下一次,信号量减1

 

 key_read3为1表示未按下,为0表示按下,每按下一次,信号量加1

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值