有没有更好的方法来使用渠道来确保同步访问地图?

时间:2017-05-30 03:16:46

标签: go maps channels

我使用Go map作为内存缓存和通道来确保同步访问。

我的“session”包将缓存定义为:map [string] * SessionData

SessionData是一个在包中定义的结构,以及代码中所见的其他访问函数。

GetWebPage(rw http.ResponseWriter, req *http.Request) {
var sd *session.SessionData
var sessTkn string

cookie, err := req.Cookie("sesstoken")

if err == nil { // cookie found
    sessTkn = cookie.Value
    // Check for cache entry for this token,
    // using a channel to protect the map and return
    // a pointer to the cached data if it exists
    sdc := make(chan *session.SessionData, 1)
    go session.GetSessionFromCache(sessTkn, sdc)
    sd = <-sdc

    if sd == nil { // sessTkn not in the cache

        // This is test data to simplify example 
        sv := make([]string, 4)
        sv[0] = sessTkn
        iv := make([]int, 3)
        iv[0] = 100
        iv[1] = 1000

        sdc := make(chan *session.SessionData, 1)
        go session.NewSessionData(sv, iv, false, false, sdc)
        session.SC[sessTkn] = <-sdc
    }

// Is this necessary? Is there a better way?
// ---------------------------------------      
    if sd == nil {
        sdc = make(chan *session.SessionData, 1)
        go session.GetSessionFromCache(sessTkn, sdc)
        sd = <-sdc      
    }
// ---------------------------------------      

    fmt.Println(sd) // just to prove that it works in both cases
}   
// The rest of the handler code follows

2 个答案:

答案 0 :(得分:4)

使用mutex保护地图。互斥体通常比使用频道和放大器更简单。用于保护资源的goroutines。

var (
  mu sync.Mutex
  cache = make(map[string]*SessionData)
)

func GetSessionFromCache(sessTkn string) *SessionData {
  mu.Lock()
  defer mu.Unlock()
  sd := cache[sessTkn]
  if sd != nil {
      return sd
  }
  sd := &SessionData{
     // initialize new value here
  }
  cache[sessTkn] = sd
  return sd
}

像这样使用:

sd := session.GetSessionFromCache(sessTkn) 

答案 1 :(得分:1)

针对此特定案例使用渠道没有额外的好处。如果您考虑一下,即使创建新频道,您仍然只能有一个可以访问地图的goroutine。由于这里没有并发优势,只需使用sync.Mutex

package session

import "sync"

var cache = struct {
  sync.Mutex
  list map[string]*SessionData
}{
  list: make(map[string]*SessionData),
}

func GetSessionFromCache(token string) *SessionData {
  cache.Lock()
  defer cache.Unlock()
  return cache.list[token]
}

然后,不需要新的goroutine。直接调用它。

sd := session.GetSessionFromCache(sessTkn)