最近看队列集,看到网上说队列集成员句柄里的消息要一次性全拿出来
我理解成是不是往队列集的同一个成员发送多次消息也只会触发一次事件呢?
还是说队列集和队列一样一次发送就触发一次事件?
动手测试一下
代码如下,两个出队列方法:
//test 消息队列集测试
//方法1,是每次只从队列集获取一个消息
printf("\r\n\r\n ********方法1,是每次只从队列集获取一个消息 ********\r\n\r\n" );
Send_data = 1 ;
xQueueSend(T_queue1, (void *)&Send_data, (TickType_t)2);
Send_data = 2 ;
xQueueSend(T_queue1, (void *)&Send_data, (TickType_t)2);
Send_data = 3 ;
xQueueSend(T_queue1, (void *)&Send_data, (TickType_t)2);
Send_data = 4 ;
xQueueSend(T_queue2, (void *)&Send_data, (TickType_t)2);
Send_data = 5 ;
xQueueSend(T_queue2, (void *)&Send_data, (TickType_t)2);
Send_data = 6 ;
xQueueSend(T_queue2, (void *)&Send_data, (TickType_t)2);
while(1)
{
HAL_Delay(100);
ActivatedQueue = xQueueSelectFromSet(T_QueueSet, 0);
if(ActivatedQueue == T_queue1)
{
result = xQueueReceive(T_queue1, &Rev_data, 0);
if( result == pdPASS )
{
printf("\r\n方法1: T_queue1 Receive = %d \r\n", Rev_data );
}
else
{
printf("\r\n方法1: xQueueReceive T_queue1 fail !!! result = %d \r\n", result );
}
}
else if(ActivatedQueue == T_queue2)
{
result = xQueueReceive(T_queue2, &Rev_data, 0);
if( result == pdPASS )
{
printf("\r\n方法1: T_queue2 Receive = %d \r\n", Rev_data );
}
else
{
printf("\r\n方法1: xQueueReceive T_queue2 fail !!! result = %d \r\n", result );
}
}
else { break; }
}
//方法2,是每次从队列集获取到句柄之后,就把该消息队列句柄里的消息全拿出来
printf("\r\n\r\n ********方法2,是每次从队列集获取到句柄之后,就把该消息队列句柄里的消息全拿出来 ********\r\n\r\n" );
Send_data = 11 ;
xQueueSend(T_queue1, (void *)&Send_data, (TickType_t)2);
Send_data = 12 ;
xQueueSend(T_queue1, (void *)&Send_data, (TickType_t)2);
Send_data = 13 ;
xQueueSend(T_queue1, (void *)&Send_data, (TickType_t)2);
Send_data = 14 ;
xQueueSend(T_queue2, (void *)&Send_data, (TickType_t)2);
Send_data = 15 ;
xQueueSend(T_queue2, (void *)&Send_data, (TickType_t)2);
Send_data = 16 ;
xQueueSend(T_queue2, (void *)&Send_data, (TickType_t)2);
while(1)
{
HAL_Delay(100);
ActivatedQueue = xQueueSelectFromSet(T_QueueSet, 0);
if(ActivatedQueue == T_queue1)
{
while( 1 )
{
result = xQueueReceive(T_queue1, &Rev_data, 0);
if( result == pdPASS )
{
printf("\r\n方法2: T_queue1 Receive = %d \r\n", Rev_data );
}
else
{
printf("\r\n方法2: T_queue1 已全部出队列 !!! result = %d \r\n", result );
break;
}
}
}
else if(ActivatedQueue == T_queue2)
{
while( 1 )
{
result = xQueueReceive(T_queue2, &Rev_data, 0);
if( result == pdPASS )
{
printf("\r\n方法2: T_queue2 Receive = %d \r\n", Rev_data );
}
else
{
printf("\r\n方法2: T_queue2 已全部出队列 !!! result = %d \r\n", result );
break;
}
}
}
else { break; }
}
//test 消息队列集测试
最后log结果如下:
********方法1,是每次只从队列集获取一个消息 ********
方法1: T_queue1 Receive = 1
方法1: T_queue1 Receive = 2
方法1: T_queue1 Receive = 3
方法1: T_queue2 Receive = 4
方法1: T_queue2 Receive = 5
方法1: T_queue2 Receive = 6
********方法2,是每次从队列集获取到句柄之后,就把该消息队列句柄里的消息全拿出来 ********
方法2: T_queue1 Receive = 11
方法2: T_queue1 Receive = 12
方法2: T_queue1 Receive = 13
方法2: T_queue1 已全部出队列 !!! result = 0
方法2: T_queue1 已全部出队列 !!! result = 0
方法2: T_queue1 已全部出队列 !!! result = 0
方法2: T_queue2 Receive = 14
方法2: T_queue2 Receive = 15
方法2: T_queue2 Receive = 16
方法2: T_queue2 已全部出队列 !!! result = 0
方法2: T_queue2 已全部出队列 !!! result = 0
方法2: T_queue2 已全部出队列 !!! result = 0
事实证明队列集还是和队列一样,每send一次就会产生一个事件.
而且方法二发现,即使我一次性把三个消息全拿出来,后面还是能再次获取队列句柄,所以是每send一次就会触发一次事件.
实践出真知
那如果只获取句柄并不取消息呢?
在使用了3次之后 xQueueSend(T_queue1, (void *)&Send_data, (TickType_t)2);
log只打了3次就没有了, 从而得知,一旦使用了 xQueueSelectFromSet 去获取队列集发生消息事件的队列句柄,就需要去该队列去取一次消息,
不取也不会再次触发事件了, 无法再得知该队列有消息进来而导致该条消息应用层面的丢失, 这应该就是我在网上看到 " 队列集成员句柄里的消息要一次性全拿出来 "的意思吧吧
其含义应该是该条消息要一次性全都拿完
测到这里,可以结束了, 只是我又想到,freertos的消息队列实际是值的拷贝,
那我们往队列集发送一串数组会怎么样呢?
稍加修改上面的代码
static QueueHandle_t T_queue1 = NULL;
static QueueHandle_t T_queue2 = NULL;
static xQueueSetHandle T_QueueSet = NULL;
void Master_EventCreat(void)
{
T_queue1 = xQueueCreate(3, sizeof(char)*100);
if(T_queue1 == NULL){
Error_Handler(__FILE__, __LINE__);
}
T_queue2 = xQueueCreate(3, sizeof(char)*100);
if(T_queue2 == NULL){
Error_Handler(__FILE__, __LINE__);
}
/* 创建多事件等待集合 */
T_QueueSet = xQueueCreateSet( 6 );
if( T_QueueSet == NULL ){
Error_Handler(__FILE__, __LINE__);
}
/* 添加消息队列和信号量到Core_QueueSet, 添加时消息队列和信号量必须是空的 */
xQueueAddToSet(T_queue1, T_QueueSet);
xQueueAddToSet(T_queue2, T_QueueSet);
}
//方法1,是每次只从队列集获取一个消息
printf("\r\n\r\n ********方法1,是每次只从队列集获取一个消息 ********\r\n\r\n" );
snprintf( Send_data, 100, " I AM head1");
snprintf( &Send_data[50], 50, " I AM test1");
xQueueSend(T_queue1, (void *)&Send_data, (TickType_t)2);
snprintf( Send_data, 100, " I AM head2");
snprintf( &Send_data[50], 50, " I AM test2");
xQueueSend(T_queue1, (void *)&Send_data, (TickType_t)2);
snprintf( Send_data, 100, " I AM head3");
snprintf( &Send_data[50], 50, " I AM test3");
xQueueSend(T_queue1, (void *)&Send_data, (TickType_t)2);
while(1)
{
HAL_Delay(100);
ActivatedQueue = xQueueSelectFromSet(T_QueueSet, 0);
if(ActivatedQueue == T_queue1)
{
result = xQueueReceive(T_queue1, &Rev_data, 0);
if( result == pdPASS )
{
printf("\r\n方法1: T_queue1 Receive[0] = %s \r\n", Rev_data );
printf("\r\n方法1: T_queue1 Receive[50] = %s \r\n\r\n", &Rev_data[50] );
}
else
{
printf("\r\n方法1: xQueueReceive T_queue1 fail !!! result = %d \r\n", result );
}
}
else { break; }
}
最后log结果如下:
所以我想xQueueSend(T_queue1, (void *)&Send_data, (TickType_t)2);是把 Send_data这个变量的 sizeof(char)*100 个字节 (因为T_queue1 = xQueueCreate(3, sizeof(char)*100);) 拷贝到自己的消息区了.
那么,问题来了,假设我们 xQueueSend 和 xQueueReceive 的变量内存小于创建该该队列单条消息的内存, 因为他是固定复制xQueueCreate时的大小, 那么可能会发生访问非法地址!!! (推测,没有验证过......)