go基础-13.线程安全和sync.map
线程安全
什么是线程安全?
现在有两个协程,同时触发,一个协程对一个全局变量进行100完成++操作,另一个对全局变量—的操作
那么,两个协程结束,最后的值应该是0才对
package main
import (
"fmt"
"sync"
)
var num int
var wait sync.WaitGroup
func add() {
for i := 0; i < 1000000; i++ {
num++
}
wait.Done()
}
func reduce() {
for i := 0; i < 1000000; i++ {
num--
}
wait.Done()
}
func main() {
wait.Add(2)
go add()
go reduce()
wait.Wait()
fmt.Println(num)
}
但是你会发现,这个输出的结果完全无法预测
这是为什么呢?
根本原因是CPU的调度方法为抢占式执行,随机调度
同步锁
那么我们能不能通过给操作加锁来解决这个问题呢
答案是可以的
package main
import (
"fmt"
"sync"
)
var num int
var wait sync.WaitGroup
var lock sync.Mutex
func add() {
// 谁先抢到了这把锁,谁就把它锁上,一旦锁上,其他的线程就只能等着
lock.Lock()
for i := 0; i < 1000000; i++ {
num++
}
lock.Unlock()
wait.Done()
}
func reduce() {
lock.Lock()
for i := 0; i < 1000000; i++ {
num--
}
lock.Unlock()
wait.Done()
}
func main() {
wait.Add(2)
go add()
go reduce()
wait.Wait()
fmt.Println(num)
}
线程安全下的map
如果我们在一个协程函数下,读写map就会引发一个错误
concurrent map read and map write
希望大家见到这个错误,就能知道,这个就是map的线程安全错误
package main
import (
"fmt"
"sync"
"time"
)
var wait sync.WaitGroup
var mp = map[string]string{}
func reader() {
for {
fmt.Println(mp["time"])
}
wait.Done()
}
func writer() {
for {
mp["time"] = time.Now().Format("15:04:05")
}
wait.Done()
}
func main() {
wait.Add(2)
go reader()
go writer()
wait.Wait()
}
我们不能在并发模式下读写map
如果要这样做
- 给读写操作加锁
- 使用sync.Map
加锁
package main
import (
"fmt"
"sync"
"time"
)
var wait sync.WaitGroup
var mp = map[string]string{}
var lock sync.Mutex
func reader() {
for {
lock.Lock()
fmt.Println(mp["time"])
lock.Unlock()
}
wait.Done()
}
func writer() {
for {
lock.Lock()
mp["time"] = time.Now().Format("15:04:05")
lock.Unlock()
}
wait.Done()
}
func main() {
wait.Add(2)
go reader()
go writer()
wait.Wait()
}
sync.Map
package main
import (
"fmt"
"sync"
"time"
)
var wait sync.WaitGroup
var mp = sync.Map{}
func reader() {
for {
fmt.Println(mp.Load("time"))
}
wait.Done()
}
func writer() {
for {
mp.Store("time", time.Now().Format("15:04:05"))
}
wait.Done()
}
func main() {
wait.Add(2)
go reader()
go writer()
wait.Wait()
}
其实看它源码,它的内部也是用了同步锁的