信号量:信号量是线程同步的手段之一。信号量的值表示可用资源数量,线程获取一次信号量,信号量的值就会减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