[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) //指定计数为2go func() {defer wg.Done()goroute(1)fmt.Println("匿名函数")}()wg.Wait() //等待计数为0,表示goroutine的线程都执行完成fmt.Println("main hello")
}
sync.Mutex互斥锁
是一个结构体.是值类型.给函数传参数的时候要传指针.
var (x = 0wg sync.WaitGrouplock sync.Mutex //定义锁
)func add() {for i := 0; i < 100000; i++ {lock.Lock() //开启锁x = x + 1lock.Unlock() //关闭锁}wg.Done()
}func main() {wg.Add(2)go add()go add()wg.Wait()fmt.Println(x)
}
sync.RWMutex读写锁
var (x = 0wg sync.WaitGrouplock sync.MutexrwLock 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 + 1time.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 mainimport ("fmt""sync"
)var (wg sync.WaitGrouponce 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 := <-ch1if !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.Mutexwg 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 writelock.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.Mutexfunc 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)
}