Golang 标准库 sync
1. Mutex
互斥锁,等同于Linux下的pthread_mutex_t
func (m *Mutex) Lock()
func (m *Mutex) Unlock()
// 多个线程同时运行,获的Mutex锁的线程优先运行,其余线程阻塞等待
func testMutex() {
mutex := sync.Mutex{}
for i := 0; i < 10; i++ {
go func(i int) {
mutex.Lock()
defer mutex.Unlock()
fmt.Println(i)
time.Sleep(time.Second)
}(i)
}
}
2. RWMutex
读写锁,等同于Linux下的pthread_rwlock_t
// 写
func (rw *RWMutex) Lock()
func (rw *RWMutex) Unlock()
// 读
func (rw *RWMutex) RLock()
func (rw *RWMutex) RUnlock()
// 写请求在读锁和写锁时都必须等待
// 读请求只在写锁时阻塞等待
func testRWMutex() {
rwMutex := sync.RWMutex{}
for i := 0; i < 10; i++ {
go func(i int) {
rwMutex.Lock()
defer rwMutex.Unlock()
fmt.Println("Write Mutex:", i)
}(i)
go func(i int) {
rwMutex.RLock()
defer rwMutex.RUnlock()
fmt.Println("Read Mutex:", i)
}(i)
}
}
3. Cond
条件变量,等同于Linux下的pthread_cond_t
type Cond struct {
noCopy noCopy
// L is held while observing or changing the condition
L Locker
notify notifyList
checker copyChecker
}
func NewCond(l Locker) *Cond
func (c *Cond) Broadcast()
func (c *Cond) Signal()
func (c *Cond) Wait()
func testCond() {
cond := sync.NewCond(&sync.Mutex{})
cond.L.Lock() // 1. 上锁
defer cond.L.Unlock()
go func() {
fmt.Println("go wait lock.")
cond.L.Lock() // 2. 等待Wait解锁
defer cond.L.Unlock() // 5. 解锁后触发 wait
defer fmt.Println("go unlock.")
fmt.Println("go locked.")
cond.Signal() // 4. 触发 wait 等待锁
}()
time.Sleep(time.Second)
fmt.Println("start wait.")
cond.Wait() // 3. 立即解锁并触发一个阻塞线程(如果没有则不触发)后立即再上锁等待Signal信号
fmt.Println("wait finished.")
}
4. WaitGroup
等待组
func (wg *WaitGroup) Add(delta int) // 新增 delta 个任务
func (wg *WaitGroup) Done() // 完成一个任务,计算器减1
func (wg *WaitGroup) Wait() // 主线程等待,直到计数器为0
// Add增加等待计数;Done减少等待计数;当计数为0时触发Wait
func testWaitGroup() {
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
time.Sleep(time.Second)
fmt.Println("go:", i)
wg.Done()
}(i)
}
fmt.Println("start wait.")
wg.Wait()
fmt.Println("wait finish.")
}
5. Once
只执行一次
type Once struct {
m Mutex
done uint32
}
func (o *Once) Do(f func())
func testOnce() {
once := sync.Once{}
for i := 0; i < 10; i++ {
go func(i int) {
once.Do(func() {
fmt.Println("Do once:", i)
})
fmt.Println("go:", i)
}(i)
}
}
6. Map
线程安全map
func (m *Map) Delete(key interface{})
func (m *Map) Load(key interface{}) (value interface{}, ok bool)
func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool)
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
func (m *Map) Range(f func(key, value interface{}) bool)
func (m *Map) Store(key, value interface{})
func testMap() {
sm := sync.Map{}
for i := 0; i < 10; i++ {
go func(i int) {
v := fmt.Sprintf("StrVal=%d", i)
_, ok := sm.LoadOrStore(i, v)
if !ok {
fmt.Println("Stored index:", i)
}
}(i)
go func(i int) {
if v, ok := sm.Load(i); ok {
fmt.Printf("Loaded %v: %v\n", i, v)
}
}(i)
}
}
7. Pool
线程安全对象池
作用:保存和复用临时对象,以减少内存分配,降低GC压力
获取对象过程:
- 固定到某个P,尝试从私有对象获取,如果私有对象非空则返回该对象,并将私有对象清掉
- 如果私有对象为空,就去当前子池的共享列表中获取(需要加锁)
- 如果当前子池列表也为空,就尝试去其他P的子池的共享列表偷一个(需要加锁)
- 如果其他子池都是空的,直接返回用户指定的New 函数对象
注意点:
- 用途仅仅是增加对象重用的几率,减少gc的负担,但依旧有不小的开销
- GC会将Pool清理掉
- Get不能保证将Put进去的全部取出来
type Pool struct {
noCopy noCopy
local unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal
localSize uintptr // size of the local array
// New optionally specifies a function to generate
// a value when Get would otherwise return nil.
// It may not be changed concurrently with calls to Get.
New func() interface{}
}
func (p *Pool) Get() interface{}
func (p *Pool) Put(x interface{})
func testPool() {
pool := &sync.Pool{
New: func() interface{} {
return -1
},
}
for i := 0; i < 10; i++ {
go func(i int) {
pool.Put(i)
}(i)
}
for i := 0; i < 20; i++ {
go func() {
v := pool.Get()
fmt.Println("Get:", v)
}()
}
}
func testPool2() {
pool := &sync.Pool{
New: func() interface{} {
return -1
},
}
for i := 0; i < 10; i++ {
pool.Put(i)
}
time.Sleep(time.Second)
// 不一定取到,且顺序不一致
for i := 0; i < 10; i++ {
fmt.Println(pool.Get())
}
}
字节拼接:
func main() {
bufferPool := &sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
// 获取缓冲区,存储字节序列
buf := bufferPool.Get().(*bytes.Buffer)
var a uint32 = 121
var b uint32 = 3434
// 将数据转为字节序列
err := binary.Write(buf, binary.LittleEndian, a)
if err != nil {
panic(err)
}
err = binary.Write(buf, binary.LittleEndian, b)
if err != nil {
panic(err)
}
// 拼接后的结果
fmt.Printf("% x\n", buf.Bytes())
// 缓冲使用完毕,必须重置并放回Pool中
buf.Reset()
bufferPool.Put(buf)
}