Go学习
1.context
2.channel
3.map
4.gmp模型
5.用多个协程交替打印abc
6.多个协程交替打印字符串和数字数组,直到字符串结束。
7.gin.Context
8.协程调度和线程有什么区别
9.sync.Map
10.sync.Mutex
11.sync.RwMutex
12.sync.WaitGroup
13.gc原理
1.context
Context可以控制一组树状结构的goroutine,相比于waitgroup,Context对派生的goroutine比waitgroup有着更强的控制能力。waitgroup适用于确定数量的goroutine,未知数量的goroutine,可采用context控制并发。Context可设置父子关系,父关闭,子也关闭。同时支持延时关闭和超时关闭。
2.channel
channel(通道)是go自带的、且唯一一个并发安全的类型。
一个通道相当于一个FIFO队列。
注意事项
1.向一个已关闭的通道发送操作,会引发panic。
2.试图关闭一个已经关闭的通道也会引发panic。
7.gin
8.协程、线程
9.sync.Map
引入原因
map类型不是并发安全的,并发读写会报fatal error
fatal error: concurrent map read and map write
case:
var testMap = map[string]string{}
func main() {
go func() {
for{
_ = testMap["bar"]
}
}()
go func() {
for {
testMap["bar"] = "foo"
}
}()
select{}
}
map为何会出现并发异常
go通过flags的hashWriting
字段来检测map是否并发异常。
查询操作:flags.hashWriting
> 0,则抛出异常。
写操作:
1.写入前检查一次标记位,通过后打上标记
2.写入完成后再检查标记位,通过后再打上标记
//各类前置操作
....
if h.flags&hashWriting != 0 {
//检查是否存在并发
throw("concurrent map writes")
}
//赋值标记位
h.flags ^= hashWriting
....
//后续操作
done:
//完成修改后,再次检查标记位
if h.flags&hashWriting == 0 {
throw("concurrent map writes")
}
//还原标记位取消hashWriting标记
h.flags &^= hashWriting
如何解决map并发问题
1.使用sync.RwMutex
type cocurrentMap = struct {
sync.RWMutex
m map[string]string
}
func main() {
var testMap = &cocurrentMap{m:make(map[string]string)}
//写
testMap.Lock()
testMap.m["a"] = "foo"
testMap.Unlock()
//读
testMap.RLock()
fmt.Println(testMap.m["a"])
testMap.RUnlock()
}
由于锁开销较大,对并发量有影响,所以推荐使用sync.Map
2.sync.Map
sync.Map的实现
空间换时间思想,同时维护两份数据,readonly&dirty,read用来避免读写冲突。
结构如下:
type Map struct {
mu Mutex //锁
read atomic.Value //readOnly
dirty map[interface{}]*entry //*entry
misses int
}
type readOnly struct {
m map[interface{}]*entry
amended bool // true if the dirty map contains some key not in m.
}
type entry struct {
p unsafe.Pointer // *interface{}
}
case:
var m sync.Map
//write
m.Store("test", 1)
m.Store(1, true)
//read
val1, _ := m.Load("test")
val2, _ := m.Load(1)
fmt.Println(val1.(int))
fmt.Println(val2.(bool))
//遍历
m.Range(func(key, value interface{}) bool {
//....
return true
})
//删除
m.Delete("test")
//读取或写入
m.LoadOrStore("test", 1)