Go sync 锁

发布时间: 更新时间: 总字数:1059 阅读时间:3m 作者: IP上海 分享 网址

介绍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
}
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数