[TOC]
#sync包-并发操作
sync.WaitGroup线程等待
使用sync.WaitGroup方法来等待其他goroutine完成
var wg sync.WaitGroup //引入sync.WaitGroup的方法
func goroute(i int) {
defer wg.Done() //计数减一
fmt.Println("hello", i)
time.Sleep(time.Second)
}
func main() {
wg.Add(2) //指定计数为2
go func() {
defer wg.Done()
goroute(1)
fmt.Println("匿名函数")
}()
wg.Wait() //等待计数为0,表示goroutine的线程都执行完成
fmt.Println("main hello")
}
sync.Mutex互斥锁
是一个结构体.是值类型.给函数传参数的时候要传指针.
var (
x = 0
wg sync.WaitGroup
lock sync.Mutex //定义锁
)
func add() {
for i := 0; i < 100000; i++ {
lock.Lock() //开启锁
x = x + 1
lock.Unlock() //关闭锁
}
wg.Done()
}
func main() {
wg.Add(2)
go add()
go add()
wg.Wait()
fmt.Println(x)
}
sync.RWMutex读写锁
var (
x = 0
wg sync.WaitGroup
lock sync.Mutex
rwLock sync.RWMutex //定义读写锁
)
func read() {
defer wg.Done()
rwLock.RLock() //开启读锁
fmt.Println(x)
time.Sleep(time.Millisecond * 2)
rwLock.RUnlock() //关闭读锁
}
func writer() {
defer wg.Done()
rwLock.Lock() //开启写锁,并行变成串行
x = x + 1
time.Sleep(time.Millisecond * 10)
rwLock.Unlock() //关闭写锁
}
func rwlockMain() {
start := time.Now()
for i := 0; i < 10; i++ {
go writer()
wg.Add(1)
}
for i := 0; i < 1000; i++ {
go read()
wg.Add(1)
}
wg.Wait()
fmt.Println(time.Now().Sub(start))
}
sync.Once只执行一次
sync.Once
其实内部包含一个互斥锁和一个布尔值,互斥锁保证布尔值和数据的安全,而布尔值用来记录初始化是否完成。这样设计就能保证初始化操作的时候是并发安全的并且初始化操作也不会被执行多次。
package main
import (
"fmt"
"sync"
)
var (
wg sync.WaitGroup
once sync.Once
)
func f1(ch1 chan<- int) {
defer wg.Done()
for i := 0; i < 100; i++ {
ch1 <- i
}
close(ch1)
}
func f2(ch1 <-chan int, ch2 chan<- int) {
defer wg.Done()
for {
x, ok := <-ch1
if !ok {
break
}
ch2 <- x * x
}
f := func() {
close(ch2)
}
once.Do(f) // 确保某个操作只执行一次
}
func main() {
a := make(chan int, 100)
b := make(chan int, 100)
wg.Add(3)
go f1(a)
go f2(a, b)
go f2(a, b)
wg.Wait()
for ret := range b {
fmt.Println(ret)
}
}
sync.Map并发安全map
普通map不支持并发
// go 内置的map不支持并发安全的,并发写入会出错
// 当并发多了之后执行上面的代码就会报fatal error: concurrent map writes错误。
var (
m = make(map[string]int)
lock sync.Mutex
wg sync.WaitGroup
)
func get(key string) int {
return m[key]
}
func set(key string, value int) {
m[key] = value
}
func main() {
for i := 0; i < 20; i++ {
wg.Add(1)
go func(n int) {
key := strconv.Itoa(n)
lock.Lock()
set(key, n) //fatal error: concurrent map read and map write
lock.Unlock()
fmt.Printf("k=:%v,v:=%v\n", key, get(key))
wg.Done()
}(i)
}
wg.Wait()
}
sync.Map支持并发
sync.Map
内置了Store
、Load
、LoadOrStore
、Delete
、Range
等操作方法。
//sync.Map 是一个支持并发安全的map
var m2 = sync.Map{}
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 21; i++ {
wg.Add(1)
go func(n int) {
key := strconv.Itoa(n)
m2.Store(key, n) // 必须使用sync.Map内置的Store方法设置键值对
value, _ := m2.Load(key) // 必须使用sync.Map提供的Load方法根据key取值
fmt.Printf("k=:%v,v:=%v\n", key, value)
wg.Done()
}(i)
}
wg.Wait()
}
sync/atomic包-原子操作
在代码中加锁操作因为涉及内核态的上下文切换会比较耗时、代价比较高。针对基本数据类型我们还可以使用原子操作来保证并发安全,因为原子操作是Go语言提供的方法它在用户态就可以完成,因此性能比加锁操作更好
// 原子操作
var x int64
var wg sync.WaitGroup
var lock sync.Mutex
func add() {
// x++
// lock.Lock()
// x = x + 1
// lock.Unlock()
atomic.AddInt64(&x, 1)
wg.Done()
}
func main() {
wg.Add(100000)
for i := 0; i < 100000; i++ {
go add()
}
wg.Wait()
fmt.Println(x)
// 比较并交换
// x = 200
// ok := atomic.CompareAndSwapInt64(&x, 200, 300) //判断x是不是等于200,如果不等于就替换为300,并返回true
// fmt.Println(ok, x)
}