目录并发不安全的例子互斥锁读写锁小结总结并发安全,就是多个并发体在同一段时间内访问同一个共享数据,共享数据能被正确处理。 很多语言的并发编程很容易在同时修改某个变量的时候,因为操作不
并发安全,就是多个并发体在同一段时间内访问同一个共享数据,共享数据能被正确处理。
很多语言的并发编程很容易在同时修改某个变量的时候,因为操作不是原子的,而出现错误计算,比如一个加法运算使用中的变量被修改,而导致计算结果出错,典型的像统计商品库存。
个人建议只要涉及到共享变量统统使用channel
,因为channel
源码中使用了互斥锁,它是并发安全的。
我们可以不用,但不可以不了解,手中有粮心中不慌。
数组是并发不安全的,在例子开始前我们要知道append
函数的行为:长度足够在原数组cap
内追加函数,增加len
,长度不够则触发扩容,申请新数组cap
增加一倍,赋值迁移。
所以在这个过程中,仅讨论扩容操作的话可能存在同时申请并赋值的情况,导致漏掉某次扩容增加的数据。
var s []int
func appendValue(i int) {
s = append(s, i)
}
func main() {
for i := 0; i < 10000; i++ { //10000个协程同时添加切片
Go appendValue(i)
}
time.Sleep(2)
fmt.Println(len(s))
}
比如上例,10000
个协程同时为切片增加数据,你可以尝试运行一下,打印出来的一定不是 10000
。
常见于控制商品减库存,控制余额增减等情况。 那么有什么办法解决竞态问题呢?
goroutine
可以访问。这两个思路贯穿了无数的高并发/分布式方案,区别是在一个进程应用中使用,还是借助某些第三方手段来实现,比如中间件。独孤九剑森罗万象一定要牢牢记住。
Go
语言中互斥锁的用法如下:
var lock sync.Mutex //互斥锁
lock.Lock() //加锁
s = append(s, i)
lock.Unlock() //解锁
在访问临界区的前后加上互斥锁,就可以保证不会出现并发问题。
我们修改还是上一个4.7.1
的例子,为其增加互斥锁。
var s []int
var lock sync.Mutex
appendValueSafe := func(i int) {
lock.Lock()
s = append(s, i)
lock.Unlock()
}
for i := 0; i < 10000; i++ { //10000个协程同时添加切片
go appendValueSafe(i)
}
time.Sleep(2)
fmt.Println(len(s))
s
的写入操作加互斥锁,保证同一时刻只有一个goroutine
修改内容。10000
,不会再出现竞态问题。互斥锁是完全互斥的,并发读没有修改的情况下是不会有问题的,也没有必要在并发读的时候加锁不然效率会变低。
用法:
rwlock sync.RWMutex
//读锁
rwlock.RLock()
rwlock.RUnlock()
//写锁
rwlock.Lock()
rwlock.Unlock()
并发读不互斥可以同时,在一个写锁获取时,其他所有锁都等待, 口诀:读读不互斥、读写互斥、写写互斥。具体测算速度的代码可以见4.7.3的源码,感兴趣的可以改下注释位置看下效率是有很明显的提升的。
sync.Mutex
, 读写锁:sync.RWMutex
都是 sync
包的。问题:只加写锁可以吗?为什么?
到此这篇关于Go并发与锁的两种方式该如何提效的文章就介绍到这了,更多相关Go并发与锁提效内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
--结束END--
本文标题: Go并发与锁的两种方式该如何提效详解
本文链接: https://lsjlt.com/news/175895.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-04-05
2024-04-05
2024-04-05
2024-04-04
2024-04-05
2024-04-05
2024-04-05
2024-04-05
2024-04-04
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0