信号量是线程间同步的一种方式。在rtthread中用于线程间同步的还有互斥量和事件集。
什么是进程间同步,简单点的类比就是工厂中的生产线,如果想要执行B工序就必须等待A工序的完成,那么工序A和工序B就是同步的关系,在程序中也是一样。只不过是工序变成了线程。在RTThread的文档里有这样的描述:同步是指按预定的先后次序进行运行,线程同步是指多个线程通过特定的机制(如互斥量,事件对象,临界区)来控制线程之间的执行顺序,也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间将是无序的。
然后就是解释一下信号量,一个经典的解释
以生活中的停车场为例来理解信号量的概念:
①当停车场空的时候,停车场的管理员发现有很多空车位,此时会让外面的车陆续进入停车场获得停车位;
②当停车场的车位满的时候,管理员发现已经没有空车位,将禁止外面的车进入停车场,车辆在外排队等候;
③当停车场内有车离开时,管理员发现有空的车位让出,允许外面的车进入停车场;待空车位填满后,又禁止外部车辆进入。
在此例子中,管理员就相当于信号量,管理员手中空车位的个数就是信号量的值(非负数,动态变化);停车位相当于公共资源(临界区),车辆相当于线程。车辆通过获得管理员的允许取得停车位,就类似于线程通过获得信号量访问公共资源。
最后信号量的使用。其实如果不追究内核的话,操作系统只需要调用api就可以了。具体就是创建信号量(rt_sem_create)、删除信号量(rt_sem_delete)获取信号量( rt_sem_take)、释放信号量( rt_sem_release)详细使用手册可以参考这里
接下来就是一个实验,使用信号量控制LED以500ms的间隔闪烁。
思路:使用一个定时器:每500毫秒释放一次信号量,在创建一个线程用来反转LED灯,当有信号量的时候就执行反转LED灯。
程序部分
/* defined the LED0 pin: PB1 */
#define LED0_PIN GET_PIN(H, 11)
//定义信号量
static rt_sem_t led_sem = RT_NULL;
//定义线程
static char led_stack[512];
static struct rt_thread led_thread;
//定时器定义
static rt_timer_t timer_res;
void task_init(void); //线程初始化函数
static void led_entry(void *parameter);//LED反转线程
static void timer(void *parameter);//定时器任务
int main(void)
{
/* set LED0 pin mode to output */
rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
task_init();
while (1)
{
rt_thread_mdelay(1000);
}
}
void task_init(void)
{
/* 创建一个动态信号量,初始值是 0,先进先出*/
led_sem = rt_sem_create("led on sem", 0, RT_IPC_FLAG_FIFO);
if (led_sem == RT_NULL)
{
rt_kprintf("create led on semaphore failed.n");
return ;
}
//静态创建任务
rt_thread_init(&led_thread, //线程句柄
"led on", //线程的描述
led_entry, //线程入口函数
RT_NULL, //线程入口参数
&led_stack[0],//线程的栈的起始地址
sizeof(led_stack),//线程的栈大小
3, 10);//线程的优先级和时间片大小
rt_thread_startup(&led_thread);//启动线程
timer_res = rt_timer_create("led sem",//定时器描述
timer,//定时器入口函数
RT_NULL,//定时器入口参数
500,//定时时间
RT_TIMER_FLAG_PERIODIC);//循环
if(timer_res != RT_NULL)
{
rt_timer_start(timer_res);//定时器开始
rt_kprintf("timer start. n");
}
}
static void timer(void *parameter)
{
rt_sem_release(led_sem);//释放信号量
}
static void led_entry(void *parameter)
{
while(1)
{
/*以永远阻塞的形式等待信号量*/
if(rt_sem_take (led_sem, RT_WAITING_FOREVER) == RT_EOK)
{
HAL_GPIO_TogglePin(GPIOH, GPIO_PIN_11);//反转LED
rt_kprintf("led toggle.tick:%d n",rt_tick_get());
}
}
}
下面就是运行结果
全部0条评论
快来发表一下你的评论吧 !