1. 现实例子
A. 多个goroutine同时操作一个资源,这个资源又叫临界区
B. 现实生活中的十字路口,通过红路灯实现线程安全
C. 火车上的厕所,通过互斥锁来实现线程安全
2. 实际例子, x = x +1
A. 先从内存中取出x的值
B. CPU进行计算,x+1
C. 然后把x+1的结果存储在内存中
3. 互斥锁介绍
A. 同时有且只有一个线程进入临界区,其他的线程则在等待锁
B. 当互斥锁释放之后,等待锁的线程才可以获取锁进入临界区
C. 多个线程同时等待同一个锁,唤醒的策略是随机的
例:有问题的代码!
package main
import (
"fmt"
"sync"
)
var x = 0
func increment(wg *sync.WaitGroup) {
x = x + 1
wg.Done()
}
func main() {
var w sync.WaitGroup
for i := 0; i < 1000; i++ {
w.Add(1)
go increment(&w)
}
w.Wait()
fmt.Println("final value of x", x)
}
使用互斥锁fix
package main
import (
"fmt"
"sync"
)
var x = 0
func increment(wg *sync.WaitGroup, m *sync.Mutex) {
m.Lock()
x = x + 1
m.Unlock()
wg.Done()
}
func main() {
var w sync.WaitGroup
var m sync.Mutex
for i := 0; i < 1000; i++ {
w.Add(1)
go increment(&w, &m)
}
w.Wait()
fmt.Println("final value of x", x)
}
4. 读写锁使用场景
A. 读多写少的场景
B. 分为两种角色,读锁和写锁
C. 当一个goroutine获取写锁之后,其他的goroutine获取写锁或读锁都会等待
D. 当一个goroutine获取读锁之后,其他的goroutine获取写锁都会等待, 但其他 goroutine获取读锁时,都会继续获得锁.
5.读写锁案例演示
6. 原子操作
A. 加锁代价比较耗时,需要上下文切换
B. 针对基本数据类型,可以使用原子操作保证线程安全
C. 原子操作在用户态就可以完成,因此性能比互斥锁要高
7.原子操作介绍