golang中的sync

1. Go语言中可以使用sync.WaitGroup来实现并发任务的同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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不是并发安全的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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等操作方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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()
 
}

  

 

posted @   专职  阅读(645)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示