golang中的sync
1. Go语言中可以使用sync.WaitGroup来实现并发任务的同步
package main import ( "fmt" "sync" ) func hello(wg *sync.WaitGroup) { defer wg.Done() fmt.Println("hello") } func main() { var wg sync.WaitGroup wg.Add(1) //go hello(wg) // 注意:wg是一个结构体,传递的时候需要传递指针,此处传递的结构体,导致wg.Wait()一直等待,死锁 go hello(&wg) wg.Wait() fmt.Println("main over") }
需要注意sync.WaitGroup是一个结构体,传递的时候要传递指针。
2. sync.Once
在编程的很多场景下我们需要确保某些操作在高并发的场景下只执行一次,例如只加载一次配置文件、只关闭一次通道等。
Go语言中的sync包中提供了一个针对只执行一次场景的解决方案–sync.Once。
sync.Once只有一个Do方法
package main
import (
"fmt"
"log"
"net"
"os"
"sync"
)
var printPid sync.Once
func main() {
listener, err := net.Listen("tcp", "127.0.0.1:18888")
if err != nil {
log.Fatalln(err)
return
}
defer listener.Close()
for {
// 注意:无论for循环遍历多少次,此处的Do方法只会执行一次
printPid.Do(func() {
fmt.Println("当前进程的ID是:", os.Getpid())
})
conn, err := listener.Accept()
if err != nil {
log.Fatalln("监听端口错误", err)
}
log.Println(conn.RemoteAddr(), "链接成功")
//go handleConn(conn) // 开启一个goroutine来处理新到来客户端链接
}
}
sync.Once结构体内部包含了一个互斥锁和布尔值,互斥锁保证布尔值和数据的安全,而布尔值用来记录初始化是否完成,
这样设计就能保证初始化操作的时候是并发安全的,并且初始化操作也不会被执行多次
3. sync.Map
go语言中内置的map不是并发安全的
package main import ( "fmt" "strconv" "sync" ) var m = make(map[string]int) func get(key string) int { return m[key] } func set(key string, value int) { m[key] = value } func main() { // 内置的map不是并发安全的 wg := sync.WaitGroup{} for i := 0; i < 5; i++ { wg.Add(1) go func(i int) { defer wg.Done() key := strconv.Itoa(i) set(key, i) fmt.Printf("key:%v, value:%v\n", key, get(key)) }(i) } wg.Wait() }
上面的代码开启少量几个goroutine的时候可能没什么问题,当并发多了之后执行上面的代码就会报fatal error: concurrent map writes错误。
像这种场景下就需要为map加锁来保证并发的安全性了,Go语言的sync包中提供了一个开箱即用的并发安全版map–sync.Map。开箱即用表示不用像内置的map一样使用make函数初始化就能直接使用。同时sync.Map内置了诸如Store、Load、LoadOrStore、Delete、Range等操作方法。
package main import ( "fmt" "strconv" "sync" ) func main() { // go语言的sync包提供了开箱即用的并发安全的map-sync.Map, // 开箱即用表示不用向内置的map一样使用make函数来初始化就能直接使用 var ( m = sync.Map{} wg sync.WaitGroup ) for i := 0; i < 20; i++ { wg.Add(1) go func(i int) { key := strconv.Itoa(i) m.Store(key, i) // map赋值 v, _ := m.Load(key) fmt.Printf("key:%v, value:%v\n", key, v) defer wg.Done() }(i) } wg.Wait() }