關於實現ID池的小討論

2021-07-24 08:46:07 字數 2063 閱讀 3290

在開發的時候遇到一種情況,就是對於一系列的請求,我需要全部加入乙個請求佇列,然後通過統一的處理程式處理完畢後,放入結果佇列。但是發出請求的程式,如果想從結果佇列中取出自己請求的結果,需要乙個全域性唯一的id,用以區分不同請求的結果。

對於這個問題,會有一些很直接的想法,比如:(1)用個count計數器,每次請求獲得count這個id,然後count加一;(2)對於每次請求,獲取請求的當前時間,然後通過一些雜湊,然後得到乙個id。

其實,就是事情固定了id的乙個範圍或者說是取值,然後不斷從這個範圍內取數當做id。

但是缺點就是count是無限往後加的,如果不考慮儲存的侷限,確實是全域性唯一的。但是要想實現無限,需要更多的記憶體來儲存這個id值,得不償失。

但是如果僅用乙個表示範圍有限的變數來儲存,必然到後面會出現溢位,這樣會與之前的id衝突。

就是id不能有效的重複利用,因為id只用於一次請求和結果,結束後應當釋放此次的id。

可能來說用時間雜湊確實在一定程度是全域性唯一的。但是雜湊在資料量很大的時候,必然會出現衝突。

時間上來說要想區分不同時間,必然需要更精確的表示時間,在能精確表示的前提下,需要的記憶體將更多。

在有限的時間精度下,進行雜湊,需要處理雜湊衝突。這種情況下,可能需要維護雜湊表,時間和空間上也就有了一定的要求。

這種想法依賴於時間的精度或雜湊,最大的問題還是衝突的問題。

第一種方法的缺點在於沒有對id進行重複利用。於是,我嘗試對第一種想法進行改進,嘗試對之前的id進行維護。

想要維護歷史釋放的id,必然需要通過一些資料結構進行維護。

其實count的活動空間就是乙個線性空間,可以假想成乙個陣列。

在一系列id釋放的操作後,理論上這個陣列中未被占用的元素可能是一段一段的。可以用鍊錶維護這樣一段一段的的範圍。

節點元素:空閒區間。

初始狀態:乙個節點,0到最大值。

獲取乙個id:遍歷鍊錶,得到第乙個空閒區間,去掉區間中的第乙個數作為id。如果區間變成空區間,需要去掉這個節點。

釋放乙個id:遍歷鍊錶,直到id在兩個相鄰區間中間,插入這個數。如果相鄰區間內合併,需要將區間合併成乙個。時間複雜度上來說,獲取和釋放的最好情況都是o(1)的,但是最差可能變成線性的複雜度。

空間上來說,最好也是o(1),但是最差也可能變成線性的。

與鍊錶不同,佇列或者棧是直接用來儲存被釋放的節點。

元素:被釋放元素。

初始狀態:count = 0, 佇列或棧為空。

獲取乙個id:先判斷佇列或者棧為空,不為空就直接從中獲取,如果為空,就獲取count,然後count加一。

釋放乙個id:如果這個id是count-1,就讓count減一,否則就放入佇列或者棧中。與鍊錶不同的是,這種方法至少維護了乙個count到最大值的連續區間,剩餘所有的數,都儲存在佇列或者棧中。

時間複雜度上來說,獲取和釋放都是o(1)的。

空間複雜度上來說,很大程度上可能會成為線性的。

假設所有請求的服務時間都是相同的話,而且是先來先服務,那麼理論上用棧是更好的,因為這種情況下,第二次被獲取的id,釋放順序應該是大致倒序的,這樣可以最大可能的等於count-1,這樣就可以減少棧中的元素了。

當然佇列應該也是有自身的好處的,本次不細研究佇列和棧的優缺點,下面將以棧為例討論。

下面的討論,本次不考慮多執行緒情況。

首先因為id是全域性唯一的。所以需要實現單例模式。

class idpool

;idpool *idpool::idpool = null;

idpool一開始應該初始化為空指標。

protected:

idpool()

建構函式應該是private或者protected的,防止使用者建立物件。

public:

static idpool *getidpool()

使用者通過getidpool獲得idpool物件。

int getid()

else

}

void freeid(int id)

未完待續…

101 內建函式id和小整數池

python語言中的變數都有自己的id,這是乙個唯一的識別符號,是乙個整數。在cpython型別直譯器中 我們用的直譯器就屬於cpython直譯器 這個整數是物件的記憶體位址。x 1print id x x 2 print id x 執行結果為 1704750560 1704750592z y x ...

記憶體池 討論為什麼 要自己實現記憶體池

lzh 花了一周時間寫記憶體池,結果和stl差不多快,各位有什麼優化策略嗎?主要是優化速度 願你此生不鎖眉 大概的實現邏輯是怎樣的 願你此生不鎖眉 現在好多自帶的記憶體池足夠用了 lzh 現在採用的演算法是和memcached類似的slabs演算法 願你此生不鎖眉 直接用他的不好麼 lzh memc...

關於c 中函式原型宣告的小討論

或許我們都知道在寫程式時如果把被調函式寫到了main 函式的後面,那麼我們就必須在main 函式的前面對被調函式的原型進行宣告。但是你知道這其中的原理嗎?先來看個程式 include using namespace std int main int a 1,b 2 int s s add a,b c...