golang传输技巧(golang系列sync同步)(1)

sync.WaitGroupsync.Oncesync.Mutex and sync.RWMutexsync.Cond

send

lock.Lock() for mailbox == 1 { sendCond.Wait() } mailbox = 1 lock.Unlock() recvCond.Signal()

recv

lock.RLock() for mailbox == 0 { recvCond.Wait() } mailbox = 0 lock.RUnlock() sendCond.Signal()

wait的操作

  1. 将goroutine加入等待通知队列
  2. 释放lock,这样允许其他goroutine操作临界资源或者更新状态到满足条件,否则永远无法满足条件
  3. 进入条件等待,goroutine阻塞
  4. 被唤醒后重新lock,抢到锁继续执行,没抢到进入锁等待;可能出现被唤醒后有一个goroutine执行了,其他goroutine又不满足条件了,所以外层要有for循环配合wait保证每次被唤醒后要检查条件是否符合要求,如果不符合继续进入wait
sync.Poolsync.Map

key and value都是interface{},但对key有要求,需要是Comparable,不支持map, slice, func,在使用sync.Map时必须自己进行类型检查

方案一

建议将sync.Map放到一个结构体中,然后为结构体提供多个方法,在方法的参数中明确参数类型,这样go编译器就可以帮助类型检测,确保类型ok

简单,但需要为每个使用到的类型定义方法,比较麻烦

方案二

也是将sync.Map放到一个结构体中,但使用reflect.Type类规定keyType和valueType,初始化结构体时指定

type ConcurrentMap struct { m sync.Map keyType reflect.Type valueType reflect.Type } func (cMap *ConcurrentMap) Load(key interface{}) (value interface{}, ok bool) { if reflect.TypeOf(key) != cMap.keyType { return } return cMap.m.Load(key) } func (cMap *ConcurrentMap) Store(key, value interface{}) { if reflect.TypeOf(key) != cMap.keyType { panic(fmt.Errorf("wrong key type: %v", reflect.TypeOf(key))) } if reflect.TypeOf(value) != cMap.valueType { panic(fmt.Errorf("wrong value type: %v", reflect.TypeOf(value))) } cMap.m.Store(key, value) }

sync.Map并发原理

type Map struct { mu Mutex // read contains the portion of the map's contents that are safe for // concurrent access (with or without mu held). // // The read field itself is always safe to load, but must only be stored with // mu held. // // Entries stored in read may be updated concurrently without mu, but updating // a previously-expunged entry requires that the entry be copied to the dirty // map and unexpunged with mu held. read atomic.Value // readOnly // dirty contains the portion of the map's contents that require mu to be // held. To ensure that the dirty map can be promoted to the read map quickly, // it also includes all of the non-expunged entries in the read map. // // Expunged entries are not stored in the dirty map. An expunged entry in the // clean map must be unexpunged and added to the dirty map before a new value // can be stored to it. // // If the dirty map is nil, the next write to the map will initialize it by // making a shallow copy of the clean map, omitting stale entries. dirty map[interface{}]*entry // misses counts the number of loads since the read map was last updated that // needed to lock mu to determine whether the key was present. // // Once enough misses have occurred to cover the cost of copying the dirty // map, the dirty map will be promoted to the read map (in the unamended // state) and the next store to the map will make a new dirty copy. misses int }

  1. 访问是先从read中找,如果没有就去dirty中找,misses记录read没有命中的次数
  2. 更新是先从read中找,如果没有就去dirty中找,不论在哪个中找到,直接update entry.p
  3. 删除是先从read中找,如果没有就去dirty中找,如果在read中entry.p=nil,如果read中没有但在dirty中,那就加锁然后delete(map, key)
,