最终直觉告诉我,这个问题可能和 Go 1.15 中 sync.Map 的改动有关(别问我为啥,真的是直觉,我也说不出来)。 示例代码
为了方便讲解,我写了一个最小可复现的代码,如下:
package main
import ( "sync" )
var sm sync.Map
func insertKeys() { keys := make([]interface{}, 0, 10) // Store some keys for i := 0; i < 10; i++ { v := make([]int, 1000) keys = append(keys, &v) sm.Store(keys[i], struct{}{}) } // delete some keys, but not all keys for i, k := range keys { if i%2 == 0 { continue } sm.Delete(k) } }
func shutdown() { sm.Range(func(key, value interface{}) bool { // do something to key return true }) }
func main() { insertKeys() // do something ... shutdown() }
Go 1.15 中 sync.Map 改动
在 Go 1.15 中,sync.Map 增加了一个方法LoadAndDelete,具体的 issue 在这:https://github.com/golang/go/issues/33762CL, 在这:https://go-review.googlesource.com/c/go/+/205899/。
// LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) { read, _ := m.read.Load().(readOnly) e, ok := read.m[key] if !ok && read.amended { m.mu.Lock() read, _ = m.read.Load().(readOnly) e, ok = read.m[key] if !ok && read.amended { e, ok = m.dirty[key] // Regardless of whether the entry was present, record a miss: this key // will take the slow path until the dirty map is promoted to the read // map. m.missLocked() } m.mu.Unlock() } if ok { return e.delete() } return nil, false }
// Delete deletes the value for a key. func (m *Map) Delete(key interface{}) { m.LoadAndDelete(key) }
func (e *entry) delete() (value interface{}, ok bool) { for { p := atomic.LoadPointer(&e.p) if p == nil || p == expunged { return nil, false } if atomic.CompareAndSwapPointer(&e.p, p, nil) { return *(*interface{})(p), true } } }
// Delete deletes the value for a key. func (m *Map) Delete(key interface{}) { read, _ := m.read.Load().(readOnly) e, ok := read.m[key] if !ok && read.amended { m.mu.Lock() read, _ = m.read.Load().(readOnly) e, ok = read.m[key] if !ok && read.amended { delete(m.dirty, key) } m.mu.Unlock() } if ok { e.delete() } }