golang實(shí)現(xiàn)循環(huán)隊(duì)列的示例代碼
概要說(shuō)明
循環(huán)隊(duì)列是一種使用固定大小的數(shù)組來(lái)實(shí)現(xiàn)隊(duì)列的數(shù)據(jù)結(jié)構(gòu),它通過(guò)循環(huán)的方式使用數(shù)組空間,具有以下好處:
- 空間高效:循環(huán)隊(duì)列避免了使用鏈表實(shí)現(xiàn)隊(duì)列時(shí)可能存在的額外空間開銷,因?yàn)殒湵淼拿總€(gè)節(jié)點(diǎn)都需要額外的存儲(chǔ)空間來(lái)保存指向下一個(gè)節(jié)點(diǎn)的指針。
- 時(shí)間效率:在循環(huán)隊(duì)列中,入隊(duì)和出隊(duì)操作通??梢栽诔?shù)時(shí)間內(nèi)完成,即O(1)時(shí)間復(fù)雜度。這是因?yàn)椴恍枰苿?dòng)隊(duì)列中的其他元素。
- 減少內(nèi)存分配:由于循環(huán)隊(duì)列使用固定大小的數(shù)組,它避免了動(dòng)態(tài)分配內(nèi)存的需要,這在某些情況下可以減少內(nèi)存分配和回收的開銷。
- 避免內(nèi)存碎片:固定大小的數(shù)組可以減少內(nèi)存碎片的產(chǎn)生,因?yàn)椴恍枰獮槊總€(gè)元素單獨(dú)分配內(nèi)存。
- 實(shí)現(xiàn)簡(jiǎn)單:循環(huán)隊(duì)列的實(shí)現(xiàn)相對(duì)簡(jiǎn)單,只需要管理兩個(gè)指針(或索引)來(lái)跟蹤隊(duì)列的頭部和尾部。
- 動(dòng)態(tài)使用:循環(huán)隊(duì)列可以動(dòng)態(tài)地使用數(shù)組空間,即使隊(duì)列滿了,也可以通過(guò)循環(huán)的方式繼續(xù)使用數(shù)組的未使用部分。
- 適用于固定大小數(shù)據(jù)集:當(dāng)數(shù)據(jù)集的大小已知且固定時(shí),循環(huán)隊(duì)列可以非常高效地使用內(nèi)存和處理數(shù)據(jù)。
- 減少錯(cuò)誤:由于循環(huán)隊(duì)列的實(shí)現(xiàn)相對(duì)簡(jiǎn)單,它減少了因復(fù)雜實(shí)現(xiàn)帶來(lái)的潛在錯(cuò)誤。
避免假溢出是循環(huán)隊(duì)列設(shè)計(jì)中的一個(gè)重要問(wèn)題。假溢出通常發(fā)生在循環(huán)隊(duì)列的頭部和尾部索引在數(shù)組中相遇時(shí),即使數(shù)組中還有空間可以存放新元素。出現(xiàn)假溢出的原因,從根本上來(lái)說(shuō),是因?yàn)檠h(huán)隊(duì)列的容量被錯(cuò)誤地判斷為已滿。
隊(duì)列常見(jiàn)問(wèn)題:假溢出
以下是幾種避免假溢出的策略:
- 使用標(biāo)志位:在循環(huán)隊(duì)列結(jié)構(gòu)中添加一個(gè)標(biāo)志位來(lái)區(qū)分隊(duì)列是真正的滿還是假溢出。當(dāng)隊(duì)列滿時(shí),設(shè)置這個(gè)標(biāo)志位;當(dāng)隊(duì)列空時(shí),清除這個(gè)標(biāo)志位。
- 使用計(jì)數(shù)器:維護(hù)一個(gè)計(jì)數(shù)器來(lái)記錄隊(duì)列中實(shí)際的元素?cái)?shù)量。入隊(duì)時(shí)增加計(jì)數(shù)器,出隊(duì)時(shí)減少計(jì)數(shù)器。即使頭部和尾部索引相遇,計(jì)數(shù)器也能正確反映隊(duì)列的狀態(tài)。
- 雙重索引:使用兩個(gè)索引來(lái)分別表示隊(duì)列的頭部和尾部,同時(shí)使用一個(gè)布爾值來(lái)區(qū)分隊(duì)列是滿還是空。當(dāng)隊(duì)列滿時(shí),尾部索引會(huì)等于頭部索引,但布爾值會(huì)標(biāo)記隊(duì)列為滿。
- 重新計(jì)算索引:在檢查隊(duì)列是否滿或空時(shí),不要僅僅依賴于頭部和尾部索引的比較,而是重新計(jì)算它們?cè)跀?shù)組中的相對(duì)位置。
- 使用額外的數(shù)組空間:在數(shù)組中預(yù)留一個(gè)額外的空間,這樣即使頭部和尾部索引相遇,也不會(huì)立即認(rèn)為隊(duì)列滿了。這種方式犧牲了一點(diǎn)空間效率,但可以簡(jiǎn)化邏輯。
- 使用環(huán)形緩沖區(qū):在某些編程語(yǔ)言的庫(kù)中,環(huán)形緩沖區(qū)(ring buffer)是一種特殊的循環(huán)隊(duì)列,它通過(guò)使用上述一些策略來(lái)避免假溢出。
- 動(dòng)態(tài)擴(kuò)容:雖然這并不直接解決假溢出問(wèn)題,但通過(guò)動(dòng)態(tài)擴(kuò)容,可以在隊(duì)列達(dá)到容量上限時(shí)創(chuàng)建一個(gè)新的更大的數(shù)組,并將舊數(shù)組中的元素復(fù)制到新數(shù)組中。
- 使用哨兵元素:在數(shù)組的末尾添加一個(gè)哨兵元素,這樣即使頭部和尾部索引相遇,哨兵元素也不會(huì)被誤認(rèn)為是隊(duì)列中的有效元素。
- 邏輯分離:在邏輯上將滿和空的狀態(tài)與頭部和尾部索引的相遇分離,通過(guò)額外的邏輯來(lái)確定隊(duì)列的真實(shí)狀態(tài)。
- 使用狀態(tài)位圖:使用位圖來(lái)表示數(shù)組中哪些位置是被占用的,這樣可以通過(guò)位圖的狀態(tài)來(lái)判斷隊(duì)列是否滿或空。
避免假溢出的關(guān)鍵在于正確地維護(hù)隊(duì)列的狀態(tài)信息,確保在任何時(shí)候都能準(zhǔn)確地判斷隊(duì)列是否真正滿了或者空了。在設(shè)計(jì)循環(huán)隊(duì)列時(shí),應(yīng)該根據(jù)具體的應(yīng)用場(chǎng)景和性能要求來(lái)選擇最合適的策略。
在Go語(yǔ)言中實(shí)現(xiàn)循環(huán)隊(duì)列,我們需要定義一個(gè)循環(huán)隊(duì)列的數(shù)據(jù)結(jié)構(gòu),并實(shí)現(xiàn)其基本操作,包括初始化、入隊(duì)(Push)、出隊(duì)(Pop)、獲取隊(duì)列長(zhǎng)度等。下面是一個(gè)簡(jiǎn)單的循環(huán)隊(duì)列實(shí)現(xiàn)示例:
循環(huán)隊(duì)列的實(shí)現(xiàn)
package QUEUE import ( "strings" "sync" ) type CycleQueue struct { data []interface{} //存儲(chǔ)空間 front int //前指針,前指針負(fù)責(zé)彈出數(shù)據(jù)移動(dòng) rear int //尾指針,后指針負(fù)責(zé)添加數(shù)據(jù)移動(dòng) cap int //設(shè)置切片最大容量 mu sync.RWMutex // 添加互斥鎖 } func NewCycleQueue(cap int) *CycleQueue { return &CycleQueue{ data: make([]interface{}, cap), cap: cap, front: 0, rear: 0, } } // 入隊(duì)操作 // 判斷隊(duì)列是否隊(duì)滿,隊(duì)滿則不允許添加數(shù)據(jù) func (q *CycleQueue) Push(data interface{}) bool { q.mu.Lock() defer q.mu.Unlock() // 確保在函數(shù)退出時(shí)釋放鎖 //check queue is full if q.QueueLength() == q.cap { //隊(duì)列已滿時(shí),不執(zhí)行入隊(duì)操作 Log.Error("push err queue is full") return false } q.data[q.rear] = data //將元素放入隊(duì)列尾部 q.rear = (q.rear + 1) % q.cap //尾部元素指向下一個(gè)空間位置,取模運(yùn)算保證了索引不越界(余數(shù)一定小于除數(shù)) return true } // 出隊(duì)操作 // 需要考慮: 隊(duì)隊(duì)為空沒(méi)有數(shù)據(jù)返回了 func (q *CycleQueue) Pop() interface{} { q.mu.Lock() defer q.mu.Unlock() if q.rear == q.front { return nil } data := q.data[q.front] q.data[q.front] = nil // 使用nil來(lái)避免內(nèi)存泄露 q.front = (q.front + 1) % q.cap return data } // 循環(huán)隊(duì)列長(zhǎng)度計(jì)算,考慮哨兵空間 func (q *CycleQueue) QueueLength() int { if q.rear >= q.front { return q.rear - q.front } return q.cap - q.front + q.rear } func (q *CycleQueue) FindDataByRequestId(requestId string) interface{} { q.mu.Lock() defer q.mu.Unlock() // 計(jì)算隊(duì)列中的有效元素?cái)?shù)量 length := q.QueueLength() // 從front指針開始遍歷隊(duì)列 index := q.front for i := 0; i < length; i++ { // 檢查當(dāng)前元素是否包含requestId if data, ok := q.data[index].(string); ok && strings.Contains(data, requestId) { // 找到元素后,從隊(duì)列中移除 q.RemoveAtIndex(index) return data } // 移動(dòng)到下一個(gè)元素 index = (index + 1) % q.cap } // 如果沒(méi)有找到,返回nil return nil } // 新增RemoveAtIndex方法,用于在循環(huán)隊(duì)列中移除指定索引的元素 func (q *CycleQueue) RemoveAtIndex(index int) { if index < 0 || index >= q.cap { return // 索引越界檢查 } // 將index位置的元素復(fù)制到最后一個(gè)元素的位置 copy(q.data[index:], q.data[index+1:q.rear]) // 更新隊(duì)列的front和rear指針 if index == q.rear { // 如果移除的是最后一個(gè)元素 q.rear = index // rear指針向前移動(dòng) } else if index < q.rear { // 如果移除的是中間的元素 q.rear-- // rear指針向前移動(dòng) } // 如果移除的是front指針前的元素 if index < q.front { q.front = index // front指針向后移動(dòng) } }
循環(huán)隊(duì)列的應(yīng)用
package server import ( "encoding/json" "strconv" "time" ) //隊(duì)列通道 type Channel struct { queue *MeetGo.CycleQueue } //隊(duì)列參數(shù)項(xiàng) type QueueItem struct { event string msg map[string]interface{} response *chan Response } //返回參數(shù) type Response struct { err interface{} result map[string]interface{} } var globalChannel *Channel //創(chuàng)建隊(duì)列通道 func NewChannel() { channel.queue = QUEUE.NewCycleQueue(1000) go channel.handle() return } //處理隊(duì)列數(shù)據(jù) func (channel *Channel) handle() { for { item := channel.queue.Pop() if item == nil { time.Sleep(40 * time.Millisecond) continue } channel.process(item) time.Sleep(1 * time.Millisecond) } } func (channel *Channel) process(inter interface{}) { item := inter.(QueueItem) } func (channel *Channel) PushSignal(item QueueItem) { Channel.queue.Push(item) } func (channel *Channel) onRequest(event string, msg map[string]interface{}) chan Response { response := make(chan Response, 1) item := QueueItem{ event, msg, &response, } channel.PushSignal(item) return response } func main(){ globalChannel = NewChannel() globalChannel.onRequset() println(response) }
到此這篇關(guān)于golang實(shí)現(xiàn)循環(huán)隊(duì)列的示例代碼的文章就介紹到這了,更多相關(guān)golang 循環(huán)隊(duì)列內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談Golang 切片(slice)擴(kuò)容機(jī)制的原理
我們知道 Golang 切片在容量不足的情況下會(huì)進(jìn)行擴(kuò)容,擴(kuò)容的原理是怎樣的呢,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06Go 語(yǔ)言中的select語(yǔ)句詳解及工作原理
在Go語(yǔ)言中,select語(yǔ)句是用于處理多個(gè)通道(channel)操作的一種控制結(jié)構(gòu),它類似于 switch 語(yǔ)句,本文給大家介紹Go 語(yǔ)言中的select語(yǔ)句詳解及工作原理,感興趣的朋友一起看看吧2025-04-04Mac GoLand打不開(閃退)也不報(bào)錯(cuò)的解決方案
這篇文章主要介紹了Mac GoLand打不開(閃退)也不報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04Go單元測(cè)試對(duì)GORM進(jìn)行Mock測(cè)試
這篇文章主要為大家介紹了Go單元測(cè)試對(duì)GORM進(jìn)行Mock測(cè)試用例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06golang 定時(shí)任務(wù)方面time.Sleep和time.Tick的優(yōu)劣對(duì)比分析
這篇文章主要介紹了golang 定時(shí)任務(wù)方面time.Sleep和time.Tick的優(yōu)劣對(duì)比分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05詳解用Go語(yǔ)言實(shí)現(xiàn)工廠模式(Golang經(jīng)典編程案例)
這篇文章主要介紹了詳解用Go語(yǔ)言實(shí)現(xiàn)工廠模式(Golang經(jīng)典編程案例),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Go?Web實(shí)戰(zhàn)之創(chuàng)建項(xiàng)目及增加日志功能
這篇文章主要為大家詳細(xì)介紹了Go?Web項(xiàng)目中如何實(shí)現(xiàn)創(chuàng)建項(xiàng)目及增加日志功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-11-11