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

Is there a better way to use channels to ensure synchronous access to the map?

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

我的 "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

使用mutex保护地图。互斥量通常比使用通道和协程来保护资源更简单。

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) 

在这种特殊情况下使用渠道没有额外的好处。如果你想一想,即使创建了新的通道,你仍然只能有一个可以访问地图的 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)