欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

并發(fā)安全本地化存儲(chǔ)go-cache讀寫鎖實(shí)現(xiàn)多協(xié)程并發(fā)訪問

 更新時(shí)間:2023年10月09日 10:57:05   作者:海生  
這篇文章主要介紹了并發(fā)安全本地化存儲(chǔ)go-cache讀寫鎖實(shí)現(xiàn)多協(xié)程并發(fā)訪問,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

簡介

go-cache廣泛使用在go語言編程中,適合迎來在單機(jī)上 存儲(chǔ)鍵值對(duì)形式的內(nèi)存緩存。

在github上地址為 https://github.com/patrickmn/go-cache他在并發(fā)的時(shí)候,線程安全(讀寫鎖) + map[string]interface{} + 過期時(shí)間 來作為go的本地化存儲(chǔ)。

這也是他的三大特性:

  • 線程安全,通過讀寫鎖支持多個(gè)協(xié)程并發(fā)訪問
  • 不需要序列化,鍵值對(duì)形式,任意值類型map[string]interface{}
  • 自定義每個(gè)key的過期時(shí)間

數(shù)據(jù)結(jié)構(gòu)

主要有Cache,以及其組成 cache,Item兩個(gè)結(jié)構(gòu)。

type Cache struct {
    *cache
    // If this is confusing, see the comment at the bottom of New()
    // 如果這令人困惑,請(qǐng)參閱New()底部的注釋。
}
type cache struct {
    defaultExpiration time.Duration
    items             map[string]Item  //存儲(chǔ)鍵值對(duì)
    mu                sync.RWMutex     // 讀寫鎖,并發(fā)安全
    onEvicted         func(string, interface{})  // 被清除時(shí)的回調(diào)函數(shù)
    janitor           *janitor         // 腳本,定期清理過期數(shù)據(jù)
}
type Item struct {
    Object     interface{} // 存儲(chǔ)的值
    Expiration int64   // 到期時(shí)間
}

創(chuàng)建Cache對(duì)象

使用New(defaultExpiration默認(rèn)過期時(shí)間, cleanupInterval定時(shí)清理時(shí)間)函數(shù)來初始化。
傳遞到兩個(gè)參數(shù):key的過期時(shí)間,以及定時(shí)腳本清理過期數(shù)據(jù)的時(shí)間。

// Return a new cache with a given default expiration duration and cleanup interval.
// 返回具有給定默認(rèn)過期和清除時(shí)間的 new cache新緩存
// If the expiration duration is less than one (or NoExpiration),
// the items in the cache never expire (by default), 
// 假如 到期時(shí)間為-1,永不過期
// and must be deleted manually.
// 并且只能手動(dòng)刪除。
// If the cleanup interval is less than one, expired items are not
// 如果清除時(shí)間小于1,過期item,在call之前是沒有被刪除的。
// deleted from the cache before calling c.DeleteExpired().
func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
    items := make(map[string]Item)
    return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
}
func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *Cache {
    c := newCache(de, m)
    // This trick ensures that the janitor goroutine (which--granted it
    // 這個(gè)代碼,確認(rèn)是否啟動(dòng) janitor看門人goroutine
    // was enabled--is running DeleteExpired on c forever) does not keep
    // the returned C object from being garbage collected. When it is
    // garbage collected, the finalizer stops the janitor goroutine, after
    // which c can be collected.
    C := &Cache{c}
    if ci > 0 {
       runJanitor(c, ci)  // 調(diào)用守衛(wèi)進(jìn)程來清理過期數(shù)據(jù)
       runtime.SetFinalizer(C, stopJanitor)
       // runtime.SetFinalizer(C, stopJanitor)會(huì)指定調(diào)用函數(shù)停止后臺(tái) goroutine,
       // 當(dāng) GC 準(zhǔn)備釋放對(duì)象時(shí),會(huì)調(diào)用stopJanitor方法,
       // Run函數(shù)中j.stop通道會(huì)輸出一個(gè)信號(hào),從而退出協(xié)程。
    }
    return C
}
func runJanitor(c *cache, ci time.Duration) {
    j := &janitor{
       Interval: ci,
       stop:     make(chan bool),
    }
    c.janitor = j
    go j.Run(c)   // 調(diào)用守衛(wèi)進(jìn)程來清理過期數(shù)據(jù)
}
// 開啟一個(gè)定時(shí)器,來定時(shí)清理數(shù)據(jù)。
func (j *janitor) Run(c *cache) {
    // 創(chuàng)建了一個(gè)計(jì)時(shí)器,時(shí)間到時(shí)ticker.C通道會(huì)輸出一個(gè)值,調(diào)用DeleteExpired()函數(shù)
    // 該函數(shù)會(huì)通過遍歷cache中的map[string]Item的過期時(shí)間,過期則直接從map中刪除,
    // 如果該值有回調(diào)函數(shù),則在刪除后執(zhí)行回調(diào)函數(shù)。
    ticker := time.NewTicker(j.Interval)
    for {
       select {
       case <-ticker.C:
          c.DeleteExpired()
       case <-j.stop:
          ticker.Stop()
          return
       }
    }
}
// Delete all expired items from the cache.
// 刪除cache中所有的過期items
// 此時(shí)會(huì)加鎖,如果定時(shí)清理的時(shí)間比較長,并且key比較多的話,
// 會(huì)導(dǎo)致一直被  清理協(xié)程鎖住。其他的協(xié)程沒法寫入。
func (c *cache) DeleteExpired() {
    var evictedItems []keyAndValue
    now := time.Now().UnixNano()
    c.mu.Lock()
    // 過期刪除的時(shí)候,需要上鎖。
    for k, v := range c.items {
       // "Inlining" of expired
       if v.Expiration > 0 && now > v.Expiration {
          ov, evicted := c.delete(k)
          if evicted {
             evictedItems = append(evictedItems, keyAndValue{k, ov})
          }
       }
    }
    c.mu.Unlock()
    // 刪除完解鎖
    for _, v := range evictedItems {
       c.onEvicted(v.key, v.value)
    }
}
func (c *cache) delete(k string) (interface{}, bool) {
    if c.onEvicted != nil {
       if v, found := c.items[k]; found {
          delete(c.items, k)
          return v.Object, true
       }
    }
    delete(c.items, k)
    return nil, false
}

Get獲取數(shù)據(jù)

// Get an item from the cache. Returns the item or nil, and a bool indicating
// whether the key was found.
// 從cache中Get一個(gè)item.返回item或者nil,和一個(gè)bool值。
func (c *cache) Get(k string) (interface{}, bool) {
    c.mu.RLock()
    // "Inlining" of get and Expired
    item, found := c.items[k]
    if !found {
       c.mu.RUnlock()
       return nil, false
    }
    // 獲取到item,是否能返回還需要判斷過期時(shí)間
    if item.Expiration > 0 {
       if time.Now().UnixNano() > item.Expiration {
          c.mu.RUnlock()
          return nil, false
       }
    }
    c.mu.RUnlock()
    return item.Object, true
}

Set保存數(shù)據(jù)

set保存數(shù)據(jù),d的表達(dá)有三種情況:

  • 為0,使用默認(rèn)的過期時(shí)間
  • 為-1,永不過期
  • 大于0的正常值,就是過期時(shí)間
// Add an item to the cache, replacing any existing item. If the duration is 0
// 將item添加到緩存中,以更換任何現(xiàn)有item。如果duration持續(xù)時(shí)間為0
// (DefaultExpiration), the cache's default expiration time is used. If it is -1
// (DefaultExpiration),使用緩存的默認(rèn)到期時(shí)間。如果是-1
// (NoExpiration), the item never expires.
// (否開發(fā)),該項(xiàng)目永遠(yuǎn)不會(huì)到期。
func (c *cache) Set(k string, x interface{}, d time.Duration) {
    // "Inlining" of set
    var e int64
    if d == DefaultExpiration {
       d = c.defaultExpiration
    }
    if d > 0 {
       e = time.Now().Add(d).UnixNano()
    }
    c.mu.Lock()
    c.items[k] = Item{
       Object:     x,
       Expiration: e,
    }
    // TODO: Calls to mu.Unlock are currently not deferred because defer
    // adds ~200 ns (as of go1.)
    c.mu.Unlock()
}

常見問題

1、高并發(fā)

因?yàn)槭羌渔i在整個(gè)cache上,相比那些加鎖在分片上的其余緩存,并發(fā)會(huì)低一些。

2、關(guān)于內(nèi)存溢出

如果設(shè)置的清理時(shí)間為0,就是永不清理,或者時(shí)間過長,有可能導(dǎo)致緩存越來越多。
因?yàn)闆]有主動(dòng)清理,占用的緩存越鬧越大。

3、關(guān)于定時(shí)清理

如果時(shí)間過長,一次清理太大,又因?yàn)榧渔i整個(gè)cache,可能會(huì)導(dǎo)致其他的協(xié)程無法寫入。

4、關(guān)于map[string]interface{}存儲(chǔ)的值,有可能會(huì)變。

interface{},如果存的是數(shù)組,或者指針等,當(dāng)取出使用的時(shí)候,修改值,會(huì)導(dǎo)致緩存中的原始值變化。

以上就是patrickmn/go-cache源碼閱讀與分析的詳細(xì)內(nèi)容,更多關(guān)于patrickmn/go-cache源碼閱讀與分析的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用go的interface案例實(shí)現(xiàn)多態(tài)范式操作

    使用go的interface案例實(shí)現(xiàn)多態(tài)范式操作

    這篇文章主要介紹了使用go的interface案例實(shí)現(xiàn)多態(tài)范式操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語言中的自定義類型你了解嗎

    Go語言中的自定義類型你了解嗎

    自定義類型是 Go 語言中非常重要的概念之一,通過自定義類型,我們可以更好地封裝數(shù)據(jù)、組織代碼,提高程序的可讀性和可維護(hù)性。本文將從以下幾個(gè)方面介紹 Go 自定義類型的相關(guān)知識(shí),感興趣的可以了解一下
    2023-04-04
  • golang 在windows中設(shè)置環(huán)境變量的操作

    golang 在windows中設(shè)置環(huán)境變量的操作

    這篇文章主要介紹了golang 在windows中設(shè)置環(huán)境變量的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Go語言中strings.HasPrefix、strings.Split、strings.SplitN()?函數(shù)

    Go語言中strings.HasPrefix、strings.Split、strings.SplitN()?函數(shù)

    本文主要介紹了Go語言中strings.HasPrefix、strings.Split、strings.SplitN()函數(shù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • 詳解Go語言中的數(shù)據(jù)類型及類型轉(zhuǎn)換

    詳解Go語言中的數(shù)據(jù)類型及類型轉(zhuǎn)換

    這篇文章主要為大家介紹了Go語言中常見的幾種數(shù)據(jù)類型,以及他們之間的轉(zhuǎn)換方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2022-04-04
  • golang實(shí)現(xiàn)協(xié)程池的方法示例

    golang實(shí)現(xiàn)協(xié)程池的方法示例

    本文主要介紹了golang實(shí)現(xiàn)協(xié)程池的方法示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • 圖文詳解Go中的channel

    圖文詳解Go中的channel

    Channel是go語言內(nèi)置的一個(gè)非常重要的特性,也是go并發(fā)編程的兩大基石之一,下面這篇文章主要給大家介紹了關(guān)于Go中channel的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • Go標(biāo)準(zhǔn)庫-ServeMux的使用與模式匹配深入探究

    Go標(biāo)準(zhǔn)庫-ServeMux的使用與模式匹配深入探究

    這篇文章主要為大家介紹了Go標(biāo)準(zhǔn)庫-ServeMux的使用與模式匹配深入探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Go?語言選擇器實(shí)例教程

    Go?語言選擇器實(shí)例教程

    這篇文章主要為大家介紹了Go?語言選擇器實(shí)例教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Golang實(shí)現(xiàn)自己的Redis數(shù)據(jù)庫內(nèi)存實(shí)例探究

    Golang實(shí)現(xiàn)自己的Redis數(shù)據(jù)庫內(nèi)存實(shí)例探究

    這篇文章主要為大家介紹了Golang實(shí)現(xiàn)自己的Redis數(shù)據(jù)庫內(nèi)存實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01

最新評(píng)論