軟件開發實踐中的入隊列和出隊列操作的C代碼示例
概述
最近有在校的學生朋友在問我,數據結構中的隊列在實際的軟件開發項目中有什么樣的用處。
大家都知道,隊列的特點是先入先出,即數據是按照入隊列的順序出隊列的。在實際的軟件開發項目中,當一個中間模塊需要接收和發送大量的消息時,隊列就可以大展身手了。我們可以將接收到的數據存儲在一個全局隊列中,然后在另外的程序流程中將數據從同一個全局隊列中取出來,經過一定的處理之后將消息發送到另外的模塊。這樣做可以降低程序的性能瓶頸。
本文用實際的C代碼示例了簡單的數據入隊列和出隊列的方法,大家可據此了解隊列的實際用法,也可參照來實現更加復雜的隊列操作。
C代碼
- /**********************************************************************
- * 版權所有 (C)2016, Zhou Zhaoxiong
- *
- * 文件名稱:QueueUse.c
- * 文件標識:無
- * 內容摘要:示例隊列的使用(入隊和出隊)
- * 其它說明:無
- * 當前版本:V1.0
- * 作 者:Zhou Zhaoxiong
- * 完成日期:20160811
- *
- **********************************************************************/
- #include <stdio.h>
- #include <string.h>
- #include <ftw.h>
- #include <pthread.h>
- #include <time.h>
- // 重定義數據類型
- typedef signed int INT32;
- typedef unsigned int UINT32;
- typedef unsigned char UINT8;
- // 宏定義
- #define MAX_QUEUE 10000 // ***隊列元素個數
- // 結構體變量
- typedef struct
- {
- UINT32 iID; // 編號
- UINT8 szInfo[100]; // 描述
- } T_StructInfo;
- // 全局變量定義
- T_StructInfo g_tQueue[MAX_QUEUE] = {0}; // 隊列結構體
- UINT32 g_iQueueHead = 0; // 隊列頭部索引
- UINT32 g_iQueueTail = 0; // 隊列尾部索引
- pthread_mutex_t g_mutex_queue_cs; // 互斥信號量
- pthread_cond_t queue_cv;
- pthread_mutexattr_t g_MutexAttr;
- // 函數聲明
- void PutDataIntoQueue(void);
- void GetDataFromQueue(void);
- INT32 EnQueue(T_StructInfo tQueueData);
- INT32 DeQueue(T_StructInfo *ptStructData);
- void Sleep(UINT32 iCountMs);
- /****************************************************************
- * 功能描述: 主函數
- * 輸入參數: 無
- * 輸出參數: 無
- * 返 回 值: 0-執行完成
- * 其他說明: 無
- * 修改日期 版本號 修改人 修改內容
- * -------------------------------------------------------------
- * 20160811 V1.0 Zhou Zhaoxiong 創建
- ****************************************************************/
- INT32 main(void)
- {
- pthread_mutex_init(&g_mutex_queue_cs, &g_MutexAttr);
- pthread_cond_init(&queue_cv, NULL);
- // 在循環中執行入隊和出隊操作
- while (1)
- {
- PutDataIntoQueue(); // 數據入隊
- Sleep(5 * 1000); // 間隔5秒
- GetDataFromQueue(); // 數據出隊
- Sleep(60 * 1000); // 每一分鐘執行一次出隊和入隊
- }
- return 0;
- }
- /****************************************************************
- * 功能描述: 將數據加入隊列中
- * 輸入參數: 無
- * 輸出參數: 無
- * 返 回 值: 0-成功 -1-失敗
- * 其他說明: 無
- * 修改日期 版本號 修改人 修改內容
- * -------------------------------------------------------------
- * 20160811 V1.0 Zhou Zhaoxiong 創建
- ****************************************************************/
- void PutDataIntoQueue(void)
- {
- T_StructInfo tQueueData = {0};
- static UINT32 iCountNum = 0;
- // 對結構體的變量進行賦值
- tQueueData.iID = iCountNum;
- snprintf(tQueueData.szInfo, sizeof(tQueueData.szInfo) - 1, "zhou%d", iCountNum);
- // 計數值累加
- iCountNum ++;
- if (iCountNum >= MAX_QUEUE-1)
- {
- iCountNum = 0;
- }
- // 將數據加入隊列(一直等到加入成功之后才退出)
- while (EnQueue(tQueueData) == -1)
- {
- Sleep(1000); // 加入失敗,1秒后重試
- }
- // 打印加入的數據
- printf("PutDataIntoQueue: ID=%d, Info=%s\n", tQueueData.iID, tQueueData.szInfo);
- }
- /****************************************************************
- * 功能描述: 將數據取出隊列中
- * 輸入參數: 無
- * 輸出參數: 無
- * 返 回 值: 0-成功 -1-失敗
- * 其他說明: 無
- * 修改日期 版本號 修改人 修改內容
- * -------------------------------------------------------------
- * 20160811 V1.0 Zhou Zhaoxiong 創建
- ****************************************************************/
- void GetDataFromQueue(void)
- {
- T_StructInfo tQueueData = {0};
- if (DeQueue(&tQueueData) == -1)
- {
- return;
- }
- // 打印取出的數據
- printf("GetDataFromQueue: ID=%d, Info=%s\n", tQueueData.iID, tQueueData.szInfo);
- }
- /****************************************************************
- * 功能描述: 數據入隊列
- * 輸入參數: tQueueData-隊列數據
- * 輸出參數: 無
- * 返 回 值: 0-成功 -1-失敗
- * 其他說明: 無
- * 修改日期 版本號 修改人 修改內容
- * -------------------------------------------------------------
- * 20160811 V1.0 Zhou Zhaoxiong 創建
- ****************************************************************/
- INT32 EnQueue(T_StructInfo tQueueData)
- {
- INT32 iRetVal = 0;
- UINT32 iNextPos = 0;
- pthread_mutex_lock(&g_mutex_queue_cs);
- iNextPos = g_iQueueTail + 1;
- if (iNextPos >= MAX_QUEUE)
- {
- iNextPos = 0;
- }
- if (iNextPos == g_iQueueHead)
- {
- iRetVal = -1; // 已達到隊列的***長度
- }
- else
- {
- // 入隊列
- memset(&g_tQueue[g_iQueueTail], 0x00, sizeof(T_StructInfo));
- memcpy(&g_tQueue[g_iQueueTail], &tQueueData, sizeof(T_StructInfo));
- g_iQueueTail = iNextPos;
- }
- pthread_cond_signal(&queue_cv);
- pthread_mutex_unlock(&g_mutex_queue_cs);
- return iRetVal;
- }
- /****************************************************************
- * 功能描述: 數據出隊列
- * 輸入參數: ptStructData-隊列數據
- * 輸出參數: 無
- * 返 回 值: 0-成功 -1-失敗
- * 其他說明: 無
- * 修改日期 版本號 修改人 修改內容
- * -------------------------------------------------------------
- * 20160811 V1.0 Zhou Zhaoxiong 創建
- ****************************************************************/
- INT32 DeQueue(T_StructInfo *ptStructData)
- {
- T_StructInfo tQueueData = {0};
- if (ptStructData == NULL)
- {
- return -1;
- }
- pthread_mutex_lock(&g_mutex_queue_cs);
- while (g_iQueueHead == g_iQueueTail)
- {
- pthread_cond_wait(&queue_cv, &g_mutex_queue_cs);
- }
- memset(&tQueueData, 0x00, sizeof(T_StructInfo));
- memcpy(&tQueueData, &g_tQueue[g_iQueueHead], sizeof(T_StructInfo));
- g_iQueueHead ++;
- if (g_iQueueHead >= MAX_QUEUE)
- {
- g_iQueueHead = 0;
- }
- pthread_mutex_unlock(&g_mutex_queue_cs);
- memcpy(ptStructData, &tQueueData, sizeof(T_StructInfo));
- return 0;
- }
- /**********************************************************************
- * 功能描述: 程序休眠
- * 輸入參數: iCountMs-休眠時間(單位:ms)
- * 輸出參數: 無
- * 返 回 值: 無
- * 其它說明: 無
- * 修改日期 版本號 修改人 修改內容
- * ------------------------------------------------------------------
- * 20160811 V1.0 Zhou Zhaoxiong 創建
- ********************************************************************/
- void Sleep(UINT32 iCountMs)
- {
- struct timeval t_timeout = {0};
- if (iCountMs < 1000)
- {
- t_timeout.tv_sec = 0;
- t_timeout.tv_usec = iCountMs * 1000;
- }
- else
- {
- t_timeout.tv_sec = iCountMs / 1000;
- t_timeout.tv_usec = (iCountMs % 1000) * 1000;
- }
- select(0, NULL, NULL, NULL, &t_timeout); // 調用select函數阻塞程序
- }
程序運行情況
我們將上面編寫好的QueueUse.c文件上傳到Linux機器上,使用“gcc -g -o QueueUseQueueUse.c”命令編譯之后,生成QueueUse文件。之后,執行“QueueUse”命令,即可看到程序的運行結果(結果會不斷地更新)如下:
- ~/zhouzx/Test/QueueUse> QueueUse
- PutDataIntoQueue: ID=0, Info=zhou0
- GetDataFromQueue: ID=0, Info=zhou0
- PutDataIntoQueue: ID=1, Info=zhou1
- GetDataFromQueue: ID=1, Info=zhou1
- PutDataIntoQueue: ID=2, Info=zhou2
- GetDataFromQueue: ID=2, Info=zhou2
- PutDataIntoQueue: ID=3, Info=zhou3
- GetDataFromQueue: ID=3, Info=zhou3
我們看到,數據先是被加入到隊列中,然后再從隊列中取出來。
程序說明
***,在本程序中,入隊列和出隊列是在同一個函數中完成的,但是,在實際開發項目的程序中,入隊列和出隊列一般是在不同的程序流程(兩個不同的線程)中完成的。
第二,本程序的數據入隊列操作是在EnQueue函數中完成的,數據出隊列操作是在DeQueue函數中完成的,全局變量g_tQueue用于存放需要處理的數據。
第三,在實際開發項目的程序中,有可能會有很多流程都會調用入隊列和出隊列的函數,為了防止多個流程同時向隊列中加入數據或取出數據,在EnQueue和DeQueue函數中使用了鎖操作。也就是說,在操作數據之前,先用pthread_mutex_lock函數執行加鎖操作,在處理完數據之后,再用pthread_mutex_unlock函數執行解鎖操作。
第四,在實際開發項目中,為了防止程序從隊列中取數據的速率過快而使得下游模塊處理不過來,我們常在從隊列取出數據之后發消息的流程中控制數據的發送速率,具體每秒鐘發送多少條可在配置文件中設置。
【本文是51CTO專欄作者周兆熊的原創作品,轉載請通過51CTO獲取作者授權】