普通的信号量存在两个弊端:一是无法避免优先级翻转问题,二是不具备实现一对多线程发送信号的能力。事件和信号量的设计初衷是一致的就是实现两个线程之间的同步,而事件比信号量更适合在多对多和多对一的场景上使用。
现有一个要求如下:main线程内要求使用消息队列堵塞接收来自传感器线程发送来的数据,并要求LED灯每隔500ms闪烁一次。
按照要求可以简单写出消息队列的程序:
while(1) { rt_err_t status = rt_mq_recv(mainqueuehandle, &pRev, sizeof(pRev), RT_WAITING_FOREVER); if(status == RT_EOK) { cnt++; if(pRev->temp > max) { max = pRev->temp; } if(pRev->temp < min) { min = pRev->temp; } avg += pRev->temp; avg /= cnt; rt_kprintf("time:%d max:%d min:%d avg:%d\n",pRev->timestamp,max,min,avg); } rt_pin_write(LED_PIN, 1- rt_pin_read(LED_PIN)); rt_thread_mdelay(500); }因此消息队列使用了堵塞等待的形式, 因此对于500ms进行LED闪烁的操作无法进行。
如果不希望通过阻塞等待的方法,那么可以将阻塞等待改变为延时等待,程序修改如下:
while(1) { rt_err_t status = rt_mq_recv(mainqueuehandle, &pRev, sizeof(pRev), 500); if(status == RT_EOK) { if(pRev->temp > max) { max = pRev->temp; } if(pRev->temp < min) { min = pRev->temp; } avg += pRev->temp; avg /=2; rt_kprintf("time:%d max:%d min:%d avg:%d\n",pRev->timestamp,max,min,avg); } rt_pin_write(LED_PIN, 1- rt_pin_read(LED_PIN)); //rt_thread_mdelay(500); }对于像执行LED闪烁这样的简单函数来说,使用延时等待的方法是可以接受的,但如果执行的其余函数时间过程和内容过大这种操作方法会导致消息队列接收数据出现实时性问题。
可以改用以下的方法来解决问题:创建一个500ms的软件定时器用于计时时间,到达之后在回调函数中直接执行发送事件的操作即可,同样需要在发送数据线程中添加首先发送事件的操作,在事件接收线程中,需要去阻塞等待事件的出现,同时检测两个事件,无论哪个事件被检测到就可直接进入分支程序运行下面的函数。
void timer_handler(void *paramter) { rt_event_send(eventhandle, T500MSENT); } int main(void) { rt_uint32_t evt = 0; DataPacket_t *pRev; static uint32_t max = 0; static uint32_t min = 0XFFFF; static uint32_t avg = 0; rt_timer_t timer; timer = rt_timer_create("500ms", timer_handler, RT_NULL, 500,RT_TIMER_FLAG_PERIODIC ); rth_sensor_create(); rth_lcd_create(); rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT); rt_timer_start(timer); while(1) { if(rt_event_recv(eventhandle, T500MSENT | DATAENT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &evt) == RT_EOK) { if(evt & DATAENT) { rt_err_t status = rt_mq_recv(mainqueuehandle, &pRev, sizeof(pRev), RT_WAITING_FOREVER); if(status == RT_EOK) { if(pRev->temp > max) { max = pRev->temp; } if(pRev->temp < min) { min = pRev->temp; } avg += pRev->temp; avg /=2; rt_kprintf("time:%d max:%d min:%d avg:%d\n",pRev->timestamp,max,min,avg); } }else if(evt & T500MSENT) { rt_pin_write(LED_PIN, 1- rt_pin_read(LED_PIN)); } } } return RT_EOK; }void rth_sensor_handler(void *paramter) { static DataPacket_t datapool[8]; uint8_t i = 0; while(1) { DataPacket_t* pData = &datapool[i]; pData->humi = rand()%100; pData->temp = rand()%50; pData->timestamp = rt_tick_get(); rt_event_send(eventhandle, DATAENT); rt_err_t status = rt_mq_send_wait(dataqueuehandle, &pData, sizeof(pData),10); rt_err_t flag = rt_mq_send_wait(mainqueuehandle, &pData, sizeof(pData),10); if(status == RT_EOK || flag == RT_EOK) { i = (i+1) % 8; } rt_thread_mdelay(1000); } }此种方法既可以解决方法1的时间延时问题还可以保持CPU占用高的问题。
由此可以总结出一个关于定时执行任务和接收消息队列组合的程序框架
while(1) { if(rt_event_recv(.....) == RT_EOK) //等待事件到达 阻塞等待 { if(evt & DATAENT) //判断 数据传输事件1 { rt_err_t status = rt_mq_recv(....); //等待消息队列 //操作接收到的数据 } else if(evt....) //判断 定时执行任务事件2 { //执行定时执行任务 } }