sync包提供了基本的同步基元,如互斥锁。除了Once和WaitGroup类型,大部分都是适用于低水平程序线程,高水平的同步使用channel通信更好一些。
type Locker
Locker接口代表一个可以加锁和解锁的对象
type Locker interface {Lock()Unlock()}
type Once
Once是只执行一次动作的对象。
func (o *Once) Do(f func()) :Do方法当且仅当第一次被调用时才执行函数f
var once sync.OnceonceBody := func() {fmt.Println("Only once")}done := make(chan bool)for i := 0; i < 10; i++ {go func() {once.Do(onceBody)done <- true}()}for i := 0; i < 10; i++ {<-done}//Only once 只打印了一次
type WaitGroup
func (wg WaitGroup) Add(delta int) //Add方法向内部计数加上delta
func (wg WaitGroup) Done() //Done方法减少WaitGroup计数器的值,应在线程的最后执行。
func (wg *WaitGroup) Wait() //Wait方法阻塞直到WaitGroup计数器减为0。
func cal(a int , b int ,n *sync.WaitGroup) {c := a+bfmt.Printf("%d + %d = %d\n",a,b,c)defer n.Done() //goroutinue完成后, WaitGroup的计数-1}func main() {var go_sync sync.WaitGroup //声明一个WaitGroup变量for i :=0 ; i<10 ;i++{go_sync.Add(1) // WaitGroup的计数加1go cal(i,i+1,&go_sync)}go_sync.Wait() //等待所有goroutine执行完毕}
type Mutex
func (m Mutex) Lock() //Lock方法锁住m,如果m已经加锁,则阻塞直到m解锁。
func (m Mutex) Unlock() //Unlock方法解锁m,如果m未加锁会导致运行时错误。锁和线程无关,可以由不同的线程加锁和解锁。
func sum(a *int,go_sync *sync.WaitGroup,go_mu *sync.Mutex) {for i:=0;i<50000;i++{go_mu.Lock()*a=*a+1go_mu.Unlock()}go_sync.Done()}func main() {var go_sync sync.WaitGroupvar go_mu sync.Mutexvar a =0go_sync.Add(2)go sum(&a,&go_sync,&go_mu)go sum(&a,&go_sync,&go_mu)go_sync.Wait()fmt.Println(a)}
type RWMutex
func (rw RWMutex) Lock() //Lock方法将rw锁定为写入状态,禁止其他线程读取或者写入。
func (rw RWMutex) Unlock() //Unlock方法解除rw的写入锁状态,如果m未加写入锁会导致运行时错误。
func (rw RWMutex) RLock() //RLock方法将rw锁定为读取状态,禁止其他线程写入,但不禁止读取。
func (rw RWMutex) RUnlock() //Runlock方法解除rw的读取锁状态,如果m未加读取锁会导致运行时错误。
var wg sync.WaitGroupvar rw sync.RWMutexvar(i=1)func write(){rw.Lock()i++time.Sleep(time.Second)rw.Unlock()wg.Done()}func read(){fmt.Println("正在等待写锁释放")rw.RLock()fmt.Println(i)time.Sleep(time.Second) //读锁是并发的,这里不会等待3srw.RUnlock()wg.Done()}func main() {go write()wg.Add(1)for i:=0;i<3;i++{wg.Add(1)go read()}wg.Wait()}正在等待写锁释放正在等待写锁释放正在等待写锁释放222
type Cond
type Cond struct {// 在观测或更改条件时L会冻结L Locker// 包含隐藏或非导出字段}func NewCond(l Locker) *Cond// 唤醒所有等待c的线程,调用本方法时,建议(非必须)保持c.L的锁定。func (c *Cond) Broadcast()// 唤醒等待c的一个线程,调用本方法时,建议(非必须)保持c.L的锁定。// 按等待队列先进先出的顺序唤醒func (c *Cond) Signal()// Wait自行解锁c.L并阻塞当前线程,在之后线程恢复执行时,Wait方法会在返回前锁定c.L。func (c *Cond) Wait()/*c.L.Lock()for !condition() {c.Wait()}... make use of condition ...c.L.Unlock()*/
type Pool
Pool可以安全的被多个线程同时使用**Pool就是为了减少GC压力的, 重复利用内存
Get与Put的之间没有任何关系
pool里的对象随时都有可能被自动移除,并且没有任何通知
type Pool struct {// 可选参数New指定一个函数在Get方法可能返回nil时来生成一个值// 该参数不能在调用Get方法时被修改New func() interface{}// 包含隐藏或非导出字段}
func (p Pool) Get() interface{}:从池中选择任意一个item,删除其在池中的引用计数,并提供给调用者
func (p Pool) Put(x interface{}):Put方法将x放入池中
个人认为的正确姿势
package abcimport ("sync")type User struct {Name stringAge int8}var pool = sync.Pool{New: func() interface{} {return new(User)},}func UsePool() {for i:=0;i<10000000;i++{user1:=pool.Get().(*User)user1.Name = "tom"user1.Age = 20。。。。。。pool.Put(user1)//注意使用完put回去,不然会一直new,增加内存开销}}
基准测试
package mainimport ("sync""testing")type Small struct {a int}var pool = sync.Pool{New: func() interface{} { return new(Small) },}//go:noinlinefunc inc(s *Small) { s.a++ }func BenchmarkWithoutPool(b *testing.B) {var s *Smallfor i := 0; i < b.N; i++ {for j := 0; j < 1000; j++ {s = &Small{ a: 1, }inc(s)}}}func BenchmarkWithPool(b *testing.B) {var s *Smallfor i := 0; i < b.N; i++ {for j := 0; j < 1000; j++ {s = pool.Get().(*Small)s.a = 1pool.Put(s)}}}

type Map
func (m Map) Store(key, value interface{}):增
func (m Map) Delete(key interface{}):删
func (m *Map) Load(key interface{}) (value interface{}, ok bool)
- 读取对应key的值,ok表示是否在map中查询到key
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
- 针对某个key的存在读取不存在就存储,loaded为true表示存在值,false表示不存在值
func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool)
- 删除并返回key,false表示不存在值
func (m *Map) Range(f func(key, value interface{}) bool)
- 表示对所有key进行遍历,并将遍历出的key,value传入回调函数进行函数调用,回调函数返回false时遍历结束,否则遍历完所有key. ```go package main import ( “sync” “fmt” )
func main() {
//开箱即用
var sm sync.Map
//store 方法,添加元素
sm.Store(1,”a”)
//Load 方法,获得value
if v,ok:=sm.Load(1);ok{
fmt.Println(v)
}
//LoadOrStore方法,获取或者保存
//参数是一对key:value,如果该key存在且没有被标记删除则返回原先的value(不更新)和true;不存在则store,返回该value 和false
if vv,ok:=sm.LoadOrStore(1,”c”);ok{
fmt.Println(vv)
}
if vv,ok:=sm.LoadOrStore(2,”c”);!ok{
fmt.Println(vv)
}
//遍历该map,参数是个函数,该函数参的两个参数是遍历获得的key和value,
返回一个bool值,当返回false时,遍历立刻结束。
sm.Range(func(k,v interface{})bool{
fmt.Print(k)
fmt.Print(“:”)
fmt.Print(v)
fmt.Println()
return true
})
}
a
a
c
1:a
2:c
```

可见随着cpu核心数的增加、并发加剧,这种读写锁+map的方式性能在不停的衰减,并且在核数为4的时候出现了性能的拐点;而sync.Map虽然性能不是特别好,但是相对比较平稳。
