实际项目中环形队列的应用与陷阱

前言

本篇讲解项目中队列的应用及注意点。
项目为君正方案家庭安防ptz ipc的IVS模块(算法处理)。使用队列来管理算法获取的消息。

代码

/*消息数据*/
typedef struct
{
    LUX_BASE_ALGOQUE_TYPE_EN   type;
    /*MOVE DETECT*/
    int   retRoi;                                       /*是否探测到*/
    int detcount;                                       /*检测出移动区域的宫格数量*/
    LUX_BASE_MOVE_REGINFO_ST    rectInfo;               /*检测初移动区域信息*/
    /*PERSON DETECT*/
    int   rectcnt;                                      /*识别人形数量*/
    LUX_PERSON_INFO person[20];             /*识别出的人形信息*/
    /*PERSON TRACKER*/
    void *IVSResult;                                    /*IVS算法处理结果*/
    void *pCallBackInfo;                                /*回调函数入口*/
    CallBackFunc pCallBackFunc;                         /*回调函数*/

}LUX_BASE_IVSDATA_ST;


/*队列结构体*/
typedef struct
{
    LUX_BASE_IVSDATA_ST IVSDetData[LUX_BASE_QUE_MAXSIZE];
    int rIdx;//read index
    int wIdx;//write indes

}LUX_BASE_IVSQUE_ST;

puch&pop

/**
 * @description: 消息队列有效数据长度
 * @param [in] pQue  队列
 * @return 0 成功,非零失败
 */
int QueueLength(LUX_BASE_IVSQUE_ST *pQue)
{
    if(NULL == pQue)
    {
        printf("%s:%d Point NULL!\n", __func__, __LINE__);
        return BASE_COMM_ERR;
    }

    return (pQue->wIdx - pQue->rIdx+ LUX_BASE_QUE_MAXSIZE) % LUX_BASE_QUE_MAXSIZE;
}
/**
 * @description: 消息入队
 * @param [in] pQue  队列
 * @param [in] pInData 出队数据
 * @return 0 成功,非零失败
 */
int PushQueue(LUX_BASE_IVSQUE_ST *pQue, LUX_BASE_IVSDATA_ST *pInData)
{
    if(NULL == pQue)
    {
        printf("%s:%d Point NULL!\n", __func__, __LINE__);
        return BASE_COMM_ERR;
    }

    /*判满*/
    if((pQue->wIdx+ 1) % LUX_BASE_QUE_MAXSIZE == pQue->rIdx)
    {
        return 0;
    }

    Thread_Mutex_Lock(&g_ivs_que_mux);
    memcpy(&pQue->IVSDetData[pQue->wIdx], pInData, sizeof(LUX_BASE_IVSDATA_ST));
    pQue->wIdx= (pQue->wIdx+ 1) % LUX_BASE_QUE_MAXSIZE;
    Thread_Mutex_UnLock(&g_ivs_que_mux);

    return 0;
}

/**
 * @description: 消息出队
 * @param [in] pQue  队列
 * @param [out] pOutData 出队数据
 * @return 0 成功,非零失败
 */
int PopQueue(LUX_BASE_IVSQUE_ST *pQue, LUX_BASE_IVSDATA_ST *pOutData)
{
    if(NULL == pQue)
    {
        printf("%s:%d Point NULL!\n", __func__, __LINE__);
        return BASE_COMM_ERR;
    }

    /*判空*/
    if(pQue->rIdx== pQue->wIdx)
    {
        printf("%s:%d Queue is empty !\n", __func__, __LINE__);
        return 0;
    }

    Thread_Mutex_Lock(&g_ivs_que_mux);
    memcpy(pOutData, &pQue->IVSDetData[pQue->rIdx], sizeof(LUX_BASE_IVSDATA_ST));
    memset(&pQue->IVSDetData[pQue->rIdx], 0, sizeof(LUX_BASE_IVSDATA_ST));
    pQue->rIdx= (pQue->rIdx+ 1) % LUX_BASE_QUE_MAXSIZE;
    Thread_Mutex_UnLock(&g_ivs_que_mux);

    return 0;
}

代码讲解

队列结构体LUX_BASE_IVSQUE_ST中有三个成员,一个为存放实际数据的结构体,另外两个为写和读的索引。
这里的索引不是地址索引,而是数组的下标索引(当然也可以用一段内存管理,索引用指针,其实都一样)

在这里插入图片描述

首先来看消息入队和消息出队的函数对队列的判断——判满、判空

判满

(pQue->wIdx + 1) % LUX_BASE_QUE_MAXSIZE == pQue->rIdx

写入队列后

pQue->wIdx = (pQue->wIdx + 1) % LUX_BASE_QUE_MAXSIZE;

使用数组管理data,索引可以就被理解为数组的下标。写索引应当比读索引快,如果写索引的下一个位置是读索引的位置,那么说明写索引已经绕了一圈回来追上读索引,此时判定为队列已满

判空

pQue->rIdx== pQue->wIdx 

出队

pQue->rIdx= (pQue->rIdx+ 1) % LUX_BASE_QUE_MAXSIZE

不难理解,读写索引在一起就是空的。两种情况,一种是刚开始的时候,全局自动初始化为0,此时是在一起的;另一种是读索引追上了写索引,队内是空的没有东西读出来的情况。

入队
检测传入的队列和队列内部情况后开始操作,直接把入队的结构体放到队列结构体的data结构体数组中
入队后写索引往后移动

出队
根据读索引把队列结构体的data结构体数组拷贝到出队结构体中,然后将出队的对接结构体的data结构体数组中对应结构体置0
出队后读索引完后移动

代码隐患讲解

在算法功能长时间运行中,处理消息的队列也在不断进行读写,读索引和写索引不断累加,使用取余可以让读写索引固定在预设范围内,但是读索引和写索引本身不在预设范围内。
因为上面的代码给的索引类型是int型,超过0b~(1 << 31)这个数字的时候,数据变为负的,那么上面的操作如读写索引的偏移就会出现问题。表现为程序跑挂掉,或者算法触发没送到队列中没上报成功。
这个问题归根究底是C语言基础问题,有符号数和无符号数的问题。

这也是实际项目中,自定义变量类型的原因之一,下面给出海思的自定义类型给大家作为参考

海思的自定义变量类型

/******************************************************************************
 Copyright (C), 2001-2011, Hisilicon Tech. Co., Ltd.
******************************************************************************
File Name     : hi_type.h
Version       : Initial Draft
Author        : Hisilicon multimedia software group
Created       : 2005/4/23
Last Modified :
Description   : The common data type defination
Function List :
History       :
1.Date        : 2008/06/28
  Author      : c42025
  Modification: modified definition for HI_S8

2.Date        :   2008/10/31
  Author      :   z44949
  Modification:   Translate the chinese comment

3.Date        :   2010/11/03
  Author      :   z44949
  Modification:   Remove some unnecessary typedef

4.Date        :   2011/01/05
  Author      :   p00123320
  Modification:   Modify definition of HI_VOID, avoid C++ compiler warning.

  
******************************************************************************/
#ifndef __HI_TYPE_H__
#define __HI_TYPE_H__


#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */

/*----------------------------------------------*
 * The common data type, will be used in the whole project.*
 *----------------------------------------------*/

typedef unsigned char           HI_U8;
typedef unsigned short          HI_U16;
typedef unsigned int            HI_U32;

typedef signed char             HI_S8;
typedef short                   HI_S16;
typedef int                     HI_S32;

/*----------------------------------------------*
 * The fixed-point data type, used to represent float data in hardware calculations.*
 *----------------------------------------------*/
/*--u8bit---------------------------------------*/
typedef unsigned char           HI_U0Q8;
typedef unsigned char           HI_U1Q7;
typedef unsigned char           HI_U5Q3;

/*--u16bit---------------------------------------*/
typedef unsigned short          HI_U0Q16;
typedef unsigned short          HI_U4Q12;
typedef unsigned short          HI_U6Q10;
typedef unsigned short          HI_U8Q8;
typedef unsigned short          HI_U12Q4;
typedef unsigned short          HI_U14Q2;

/*--s16bit---------------------------------------*/
typedef short                   HI_S9Q7;
typedef short                   HI_S14Q2;
typedef short                   HI_S1Q15;

/*--u32bit---------------------------------------*/
typedef unsigned int            HI_U22Q10;
typedef unsigned int            HI_U25Q7;

/*--s32bit---------------------------------------*/
typedef int                     HI_S25Q7;
typedef int                     HI_S16Q16;

/*----------------------------------------------*
 * The fixed-point data type combine with FLAG bits.*
 *----------------------------------------------*/

/*8bits unsigned integer,4bits decimal fraction,4bits flag bits*/
typedef unsigned short          HI_U8Q4F4;

/*float*/
typedef float               HI_FLOAT;
/*double*/
typedef double                  HI_DOUBLE;


#ifndef _M_IX86
    typedef unsigned long long  HI_U64;
    typedef long long           HI_S64;
#else
    typedef __int64             HI_U64;
    typedef __int64             HI_S64;
#endif

typedef char                    HI_CHAR;
#define HI_VOID                 void

/*----------------------------------------------*
 * const defination                             *
 *----------------------------------------------*/
typedef enum {
    HI_FALSE = 0,
    HI_TRUE  = 1,
} HI_BOOL;

#ifndef NULL
    #define NULL    0L
#endif

#define HI_NULL     0L
#define HI_SUCCESS  0
#define HI_FAILURE  (-1)


#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */

#endif /* __HI_TYPE_H__ */
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
环形队列是一种特殊的队列数据结构,它可以利用数组来实现循环存储。在C语言环形队列常用于需要循环取出数据的场景,例如音乐播放器的缓冲区、生产者消费者模型的消息队列等。 下面以生产者消费者模型为例,介绍一下如何使用环形队列: 1. 定义环形队列的结构体,包含队列的大小、队头和队尾指针、队列元素数组等成员变量。 ``` #define QUEUE_SIZE 10 typedef struct { int data[QUEUE_SIZE]; int front; // 队头指针 int rear; // 队尾指针 } CircularQueue; ``` 2. 初始化队列,将队头和队尾指针都设置为0。 ``` void initQueue(CircularQueue *queue) { queue->front = 0; queue->rear = 0; } ``` 3. 实现入队操作,将元素插入到队尾,并更新队尾指针。如果队列已满,则返回-1表示插入失败。 ``` int enqueue(CircularQueue *queue, int value) { int nextRear = (queue->rear + 1) % QUEUE_SIZE; // 计算下一个队尾指针 if (nextRear == queue->front) { // 队列已满 return -1; } queue->data[queue->rear] = value; queue->rear = nextRear; return 0; } ``` 4. 实现出队操作,从队头取出元素,并更新队头指针。如果队列为空,则返回-1表示取出失败。 ``` int dequeue(CircularQueue *queue) { if (queue->front == queue->rear) { // 队列为空 return -1; } int value = queue->data[queue->front]; queue->front = (queue->front + 1) % QUEUE_SIZE; // 更新队头指针 return value; } ``` 5. 在生产者消费者模型,生产者向队列插入数据,消费者从队列取出数据。生产者和消费者都需要持有队列的指针。 ``` void *producer(void *arg) { CircularQueue *queue = (CircularQueue *)arg; for (int i = 0; i < 100; i++) { int ret = enqueue(queue, i); if (ret == -1) { printf("producer: queue is full\n"); } else { printf("producer: enqueue %d\n", i); } } pthread_exit(NULL); } void *consumer(void *arg) { CircularQueue *queue = (CircularQueue *)arg; for (int i = 0; i < 100; i++) { int value = dequeue(queue); if (value == -1) { printf("consumer: queue is empty\n"); } else { printf("consumer: dequeue %d\n", value); } } pthread_exit(NULL); } int main() { CircularQueue queue; initQueue(&queue); pthread_t producerThread, consumerThread; pthread_create(&producerThread, NULL, producer, &queue); pthread_create(&consumerThread, NULL, consumer, &queue); pthread_join(producerThread, NULL); pthread_join(consumerThread, NULL); return 0; } ``` 上面的代码,生产者往队列插入数据,消费者从队列取出数据,当队列已满时,生产者会输出"producer: queue is full",当队列为空时,消费者会输出"consumer: queue is empty"。通过使用环形队列,我们可以实现循环存储,并且不需要移动元素,提高了队列操作的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Spark!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值