亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

并發(fā)安全本地化存儲go-cache讀寫鎖實現多協程并發(fā)訪問

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

簡介

go-cache廣泛使用在go語言編程中,適合迎來在單機上 存儲鍵值對形式的內存緩存。

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

這也是他的三大特性:

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

數據結構

主要有Cache,以及其組成 cache,Item兩個結構。

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

創(chuàng)建Cache對象

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

// Return a new cache with a given default expiration duration and cleanup interval.
// 返回具有給定默認過期和清除時間的 new cache新緩存
// If the expiration duration is less than one (or NoExpiration),
// the items in the cache never expire (by default), 
// 假如 到期時間為-1,永不過期
// and must be deleted manually.
// 并且只能手動刪除。
// If the cleanup interval is less than one, expired items are not
// 如果清除時間小于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
    // 這個代碼,確認是否啟動 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)  // 調用守衛(wèi)進程來清理過期數據
       runtime.SetFinalizer(C, stopJanitor)
       // runtime.SetFinalizer(C, stopJanitor)會指定調用函數停止后臺 goroutine,
       // 當 GC 準備釋放對象時,會調用stopJanitor方法,
       // Run函數中j.stop通道會輸出一個信號,從而退出協程。
    }
    return C
}
func runJanitor(c *cache, ci time.Duration) {
    j := &janitor{
       Interval: ci,
       stop:     make(chan bool),
    }
    c.janitor = j
    go j.Run(c)   // 調用守衛(wèi)進程來清理過期數據
}
// 開啟一個定時器,來定時清理數據。
func (j *janitor) Run(c *cache) {
    // 創(chuàng)建了一個計時器,時間到時ticker.C通道會輸出一個值,調用DeleteExpired()函數
    // 該函數會通過遍歷cache中的map[string]Item的過期時間,過期則直接從map中刪除,
    // 如果該值有回調函數,則在刪除后執(zhí)行回調函數。
    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
// 此時會加鎖,如果定時清理的時間比較長,并且key比較多的話,
// 會導致一直被  清理協程鎖住。其他的協程沒法寫入。
func (c *cache) DeleteExpired() {
    var evictedItems []keyAndValue
    now := time.Now().UnixNano()
    c.mu.Lock()
    // 過期刪除的時候,需要上鎖。
    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獲取數據

// Get an item from the cache. Returns the item or nil, and a bool indicating
// whether the key was found.
// 從cache中Get一個item.返回item或者nil,和一個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,是否能返回還需要判斷過期時間
    if item.Expiration > 0 {
       if time.Now().UnixNano() > item.Expiration {
          c.mu.RUnlock()
          return nil, false
       }
    }
    c.mu.RUnlock()
    return item.Object, true
}

Set保存數據

set保存數據,d的表達有三種情況:

  • 為0,使用默認的過期時間
  • 為-1,永不過期
  • 大于0的正常值,就是過期時間
// Add an item to the cache, replacing any existing item. If the duration is 0
// 將item添加到緩存中,以更換任何現有item。如果duration持續(xù)時間為0
// (DefaultExpiration), the cache's default expiration time is used. If it is -1
// (DefaultExpiration),使用緩存的默認到期時間。如果是-1
// (NoExpiration), the item never expires.
// (否開發(fā)),該項目永遠不會到期。
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ā)

因為是加鎖在整個cache上,相比那些加鎖在分片上的其余緩存,并發(fā)會低一些。

2、關于內存溢出

如果設置的清理時間為0,就是永不清理,或者時間過長,有可能導致緩存越來越多。
因為沒有主動清理,占用的緩存越鬧越大。

3、關于定時清理

如果時間過長,一次清理太大,又因為加鎖整個cache,可能會導致其他的協程無法寫入。

4、關于map[string]interface{}存儲的值,有可能會變。

interface{},如果存的是數組,或者指針等,當取出使用的時候,修改值,會導致緩存中的原始值變化。

以上就是patrickmn/go-cache源碼閱讀與分析的詳細內容,更多關于patrickmn/go-cache源碼閱讀與分析的資料請關注腳本之家其它相關文章!

相關文章

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

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

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

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

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

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

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

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

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

    詳解Go語言中的數據類型及類型轉換

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

    golang實現協程池的方法示例

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

    圖文詳解Go中的channel

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

    Go標準庫-ServeMux的使用與模式匹配深入探究

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

    Go?語言選擇器實例教程

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

    Golang實現自己的Redis數據庫內存實例探究

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

最新評論