主内容
关于这个问题首先我们得明确:
- 代码中的加锁操作因为涉及内核态的上下文切换会比较耗时、代价比较高。针对基本数据类型,我们还可以使用原子操作来保证并发安全,因为原子操作是Go语言提供的方法它在用户态就可以完成,因此性能比加锁操作更好。
- 用户态和内核态的区别:
-
用户态:在⽤户态下,进程或程序只能访问受限的资源和执⾏受限的指令集,不能直接访问操作系统的核⼼部分,也不能直接访问硬件资源,⽤户态下的 CPU 不允许独占,也就是说 CPU 能够被其他程序获取。
-
核⼼态:核⼼态是操作系统的特权级别,允许进程或程序执⾏特权指令和访问操作系统的核⼼部分。在核⼼态下,进程可以直接访问硬件资源,执⾏系统调⽤,管理内存、⽂件系统等操作。处于内核态的 CPU 可以从⼀个程序切换到另外⼀个程序,并且占⽤ CPU 不会发⽣抢占情况,⼀般处于特权级 0 的状态我们称之为内核态。
但是运行下列代码时你会发现原子操作性能比加锁操作性能低:
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
var x int64
var l sync.Mutex
var wg sync.WaitGroup
// 普通版加函数
func add() {
// x = x + 1
x++ // 等价于上面的操作
wg.Done()
}
// 互斥锁版加函数
func mutexAdd() {
l.Lock()
x++
l.Unlock