sync.Mutex/RWMutex
- channel一般用于解决逻辑层面的并发处理,而锁用来保护局部变量的数据安全
文章图片
- 重复加锁会引发死锁,重复解锁会引发panic
-
sync.Mutex
作为参数时的时候需要传指针,不然就是拷贝,会引起加锁失败
- 作为嵌入类型时应该嵌入指针
- 因为调用值接受者的方法是拷贝
type data struct {
sync.Mutex
}func (d data) test(s string) {
d.Lock()
defer d.Unlock()
for i :=0;
i < 3;
i++ {
fmt.Println(s)
time.Sleep(time.Second)
}
}// 调用
var d data
go func() {d.test("read")}()
go func() {d.test("write")}()// 输出:write和read交替输出,并没有实现加锁
// 改为指针接受者或者指针内嵌可以解决该锁失效问题
sync.WaitGroup
- 适合实现一对多的 goroutine 协作流程
sync.waitGroup
作为参数时,应该传入指针,或者Done()
方法- 计数器的值不能小于0, 这样会引发一个 panic
- 不适当地调用这类值的Done方法和Add方法都会如此
func main() {
var wg sync.WaitGroup
wg.Add(2)
go f(&wg)
go g(wg.Done)
wg.Wait()
}func f(wg *sync.WaitGroup) {
defer wg.Done()
//do something
}func g(done func()) {
defer done()
//do something
}
sync.Map
- 内置map多线程不安全,多个线程同时写入map会panic
- 并且该panic不能被recover
- 解决方案
- 使用
sync.RWMutex
加锁- 不推荐,锁消耗较大
- 使用线程安全的
sync.Map
- 适合读多写少
- 用空间换时间,内存占用较大
- 算法复杂度与map类型一样都是O(1)
- 使用 concurrent map
- 适合读写都频繁
- 分段加锁map
- 例如: https://github.com/orcaman/concurrent-map
- 使用
文章图片
sync.Map
的键和值的类型都是interface{}
- 由于这些键值的实际类型只有在程序运行期间才能够确定,所以无法在编译期对它们进行检查
- 键类型不能是函数类型、字典类型和切片类型
- 不正确的键值实际类型会引发 panic
- 本身确实也用到了锁,但是,它会尽可能地避免使用锁
sync.Map
类型在内部使用了大量的原子操作来存取键和值,并使用了两个原生的map作为存储介质- 这两个原生字典一个被称为只读字典,另一个被称为脏字典
- 空间换时间。通过冗余的两个数据结构(read、dirty),实现加锁对性能的影响
- 使用只读数据(read),避免读写冲突
- 动态调整,miss次数多了之后,将dirty数据提升为read
- 延迟删除。 删除一个键值只是打标记,只有在提升dirty的时候才清理删除的数据
- 优先从read读取、更新、删除,因为对read的读取不需要锁
文章图片
文章图片
sync.Once
sync.Once
只计算Do被调用的次数,而不是调用传入Do的唯一函数的次数
- 复用对象,例如切片等
- 对象的缓存有效期为下一次GC之前
- 生命周期受 GC 影响,不适合于做连接池等
文章图片
- 生命周期受 GC 影响,不适合于做连接池等
- 条件变量并不是被用来保护临界区和共享资源的,它是用于控制想要访问共享资源线程的顺序
- 当共享资源的状态发生变化时,它可以被用来通知被互斥锁阻塞的线程
- 条件变量是基于互斥锁或读写锁的,必须有他们的支持才能够起作用
- 参考:https://www.kancloud.cn/mutouzhang/go/596828
- 执行速度要比其他的同步工具快得多,通常会高出好几个数量级
- 但由于原子操作函数只支持非常有限的数据类型,所以在很多应用场景下,互斥锁往往是更加适合的
- 操作系统层面只对针对二进制位或整数的原子操作提供了支持
- 因为原子操作不能被中断,所以它需要足够简单,并且要求快速
- 如果原子操作迟迟不能完成,而它又不会被中断,将会给计算机执行指令的效率带来巨大的负面影响
- 操作系统层面只对针对二进制位或整数的原子操作提供了支持
sync/atomic
包中的函数可以做的原子操作有: 加法(add)、比较并交换(compare and swap,简称 CAS)、加载(load)、存储(store)和交换(swap)- CAS 操作,是有条件的交换操作,只有在条件满足的情况下才会进行值的交换
推荐阅读
- 【golang】leetcode中级-字母异位词分组&无重复字符的最长子串
- 彻底理解Golang Map
- kratos线上开源年会它来啦~
- 深入浅出 Golang 资源嵌入方案(go-bindata篇)
- 深入浅出 Golang 资源嵌入方案(前篇)
- golang 经典案例总结
- Go实战 | 基于有向无环图的并发执行流的实现
- Golang 数组和切片
- Go JSON编码与解码()
- golang map基础知识