介绍Golang中sync库中的各种锁的使用。
介绍
sync.Cond
条件锁sync.Mutex
互斥锁sync.Once
只执行一次的锁,代码位于 ${GOROOT}/src/sync/once.go
sync.Pool
对象池sync.atomic
原子锁- sync/errgroup 为多个子任务组成的 goroutines 组提供同步、错误传播和上下文取消功能
func (g *Group) Go(f func() error)
启动一个协程func (g *Group) Wait() error
等待所有协程结束
sync.RWMutex
读写互斥锁Lock|Unlock
RLock|RUnlock
- 说明:
- 任意时刻只能加一把写锁,且不能加读锁
- 没加写锁时,可以加多把读锁;读锁加上时,不能再加写锁
sync.WaitGroup
等待组锁,用来计数- 说明
- go 不支持可重入锁
Lock()
之前使用 Unlock()
会导致 panic
异常
sync.Pool
sync.Pool
保存和复用临时对象,减少内存分配,降低 GC 压力
import (
"hash"
"sync"
)
var (
md5Pool = sync.Pool{New: func() interface{} { return md5.New() }}
sha256Pool = sync.Pool{New: func() interface{} { return sha256.New() }}
)
// 使用
has := md5Pool.Get().(hash.Hash)
has.XXX
md5Pool.Put(has)
sync.Map
- 并发使用的安全性
- 数组、slice、struct 允许并发修改(可能会发生脏数据)
- sync.Map 线程安全的 Map(不推荐使用 map 并发操作),没有
Len
方法,需要通过 func (*Map) Range
自己计算
死锁
fatal error: all goroutines are asleep - deadlock!
- 产生原因
- 无缓存
chan
没有读,可以通过 goroutines 去读或建立有缓存的 chan
package main
func main() {
// ch1 := make(chan string)
// ch1 <- "hello world"
ch := make(chan int,1)
ch<-1
ch<-1
println(<-ch)
}
示例
Once 示例
sync.Once
修改的函数只能执行一次,一般定义为全局的,用在项目启动时限制一次性的初始化函数,如单例模式。
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once // 需要传递一个函数进去
fmt.Printf("%T %#v\n", once, once)
for i := 0; i < 10; i++ {
once.Do(func() {
fmt.Println(i)
})
}
// Output:
//sync.Once sync.Once{done:0x0, m:sync.Mutex{state:0, sema:0x0}}
//0
}
Map 示例
sync.Map
是 Go 的 map[interface{}]interface{}
类型,但对于多个 goroutine
的并发使用是安全的,不需要额外的锁定或协调。
package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
m.Store("k1", "str")
m.Store("k2", 2)
if v, ok := m.Load("k1"); ok {
fmt.Println(v.(string), ok)
} else {
fmt.Println(ok)
}
if v, ok := m.Load("k2"); ok {
fmt.Println(v.(int), ok)
} else {
fmt.Println(ok)
}
if v, ok := m.Load("k10"); ok {
fmt.Println(v, ok)
} else {
fmt.Println(ok)
}
m.Delete("k1")
if v, ok := m.Load(("k1")); ok {
fmt.Println(v.(string), ok)
} else {
fmt.Println(ok)
}
// Output:
//str true
//2 true
//false
//false
}
对象池
sync.Pool
是一组可以单独保存和检索的临时对象。
package main
import (
"fmt"
"sync"
)
func main() {
p := sync.Pool{
New: func() interface{} {
fmt.Println("call new")
return 1
},
}
// 从池里获取一个
x := p.Get()
fmt.Printf("%T %#v\n", x, x)
// 放回池
p.Put(x)
// 再次从池里获取,不调用新建
x = p.Get()
fmt.Printf("%T %#v\n", x, x)
// 在调用会创建,池里已经没有了,会重新创建
x = p.Get()
fmt.Printf("%T %#v\n", x, x)
// Output:
//call new
//int 1
//int 1
//call new
//int 1
}
指定义对象池
package main
import (
"fmt"
"sync"
)
type New func() interface{}
type Pool struct {
mutex sync.Mutex
objects []interface{} // 切片
new New
}
func NewPool(new New) *Pool {
return &Pool{
objects: make([]interface{}, 0),
new: new,
}
}
func NewSizePool(new New, size int) *Pool {
// 初始化池的数量
objects := make([]interface{}, size)
for i := 0; i < size; i++ {
objects[i] = new()
}
return &Pool{
objects: objects,
new: new,
}
}
func (p *Pool) Get() interface{} {
p.mutex.Lock()
defer p.mutex.Unlock()
var result interface{}
if len(p.objects) > 0 {
// 队列方式获取
result = p.objects[0]
p.objects = p.objects[1:]
// 堆栈方式获取
//l := len(p.objects)
//result = p.objects[l-1]
//p.objects = p.objects[:l-1]
return result
} else {
// 如果没有就新建
return p.new()
}
}
func (p *Pool) Put(obj interface{}) {
p.mutex.Lock()
defer p.mutex.Unlock()
p.objects = append(p.objects, obj)
}
func main() {
p := NewPool(func() interface{} {
fmt.Println("call new")
return 1
})
// 从池里获取一个
x := p.Get()
fmt.Printf("%T %#v\n", x, x)
// 放回池
p.Put(x)
// 再次从池里获取,不调用新建
x = p.Get()
fmt.Printf("%T %#v\n", x, x)
// 在调用会创建,池里已经没有了,会重新创建
x = p.Get()
fmt.Printf("%T %#v\n", x, x)
fmt.Println()
p = NewSizePool(func() interface{} {
fmt.Println("call new 2")
return 2
}, 2)
for i := 0; i < 3; i++ {
x = p.Get()
fmt.Printf("%T %#v\n", x, x)
}
// Output:
//call new
//int 1
//int 1
//call new
//int 1
//
//call new 2
//call new 2
//int 2
//int 2
//call new 2
//int 2
}