介绍Golang中sync库中的各种锁的使用。
介绍
- sync.Cond 条件锁
- sync.Mutex 互斥锁
- sync.Once 只执行一次的锁
- sync.Pool 对象池
- sync.atomic 原子锁
- sync.RWMutex 读写互斥锁
- Lock|Unlock
- RLock|RUnlock
- 说明:
- 任意时刻只能加一把写锁,且不能加读锁
- 没加写锁时,可以加多把读锁;读锁加上时,不能再加写锁
- sync.WaitGroup 等待组锁,用来计数
sync.Map
- 并发使用的安全性
- 数组、slice、struct 允许并发修改(可能会发生脏数据)
- sync.Map 线程安全的 Map(不推荐使用 map 并发操作),没有
Len
方法,需要通过 func (*Map) Range
自己计算
示例
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
}