G

[Golang基础] Sync包

RoLingG Golang 2024-10-20

Sync包

  • sync.WaitGroup

    这个在并发编程方面很常用,基本多个goroutine

  • sync.Once

    在编程的很多场景下我们需要确保某些操作在高并发的场景下只执行一次,例如只加载一次配置文件、只关闭一次通道等。

    Go语言中的sync包中提供了一个针对只执行一次场景的解决方案–sync.Once。

    sync.Once只有一个Do方法,其签名如下:

    func (o *Once) Do(f func()) {}

    注意:如果要执行的函数f需要传递参数就需要搭配闭包来使用。

  • sync.Map

    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() {
        wg := sync.WaitGroup{}
        for i := 0; i < 20; i++ {
            wg.Add(1)
            go func(n int) {
                key := strconv.Itoa(n)
                set(key, n)
                fmt.Printf("k=:%v,v:=%v\n", key, get(key))
                wg.Done()
            }(i)
        }
        wg.Wait()
    }

    上面的代码开启少量几个goroutine的时候可能没什么问题,当并发多了之后执行上面的代码就会报fatal error: concurrent map writes错误,这个就是map的线程安全错误,我们不能在并发模式下读写map。

    像这种场景下就需要为map加锁来保证并发的安全性了,Go语言的sync包中提供了一个开箱即用的并发安全版map – sync.Map。开箱即用表示不用像内置的map一样使用make函数初始化就能直接使用。同时sync.Map内置了诸如Store、Load、LoadOrStore、Delete、Range等操作方法。

    var m = sync.Map{}
    
    func main() {
        wg := sync.WaitGroup{}
        for i := 0; i < 20; i++ {
            wg.Add(1)
            go func(n int) {
                key := strconv.Itoa(n)
                m.Store(key, n)
                value, _ := m.Load(key)
                fmt.Printf("k=:%v,v:=%v\n", key, value)
                wg.Done()
            }(i)
        }
        wg.Wait()
    }

    sync.Map 是 Go 语言中提供的一个并发安全的 map 类型,它允许多个 goroutine 安全地使用 map 进行读写操作而不需要使用互斥锁(mutex)。以下是 sync.Map 提供的一些主要操作方法及其功能:

    1. Store

      • 功能:将给定的键和值存储到 map 中。如果键已经存在,则会更新其对应的值。
      • 用法示例:m.Store(key, value)
    2. Load

      • 功能:返回 map 中指定键的值。如果键不存在,则返回 nil。
      • 用法示例:value, ok := m.Load(key),其中 ok 表示键是否存在。
    3. LoadOrStore

      • 功能:如果给定的键存在于 map 中,则返回其值;如果键不存在,则将给定的值存储到 map 中,并返回该值。
      • 用法示例:value, loaded := m.LoadOrStore(key, value)loaded 表示键是否已经存在。
    4. Delete

      • 功能:从 map 中删除指定的键及其对应的值。
      • 用法示例:m.Delete(key)
    5. Range

      • 功能:遍历 map 中的所有键值对。Range 方法接受一个函数作为参数,该函数会被调用,传入当前遍历到的键和值。如果函数返回 false,则停止遍历。
      • 用法示例:

        m.Range(func(key, value interface{}) bool {
            fmt.Printf("Key: %v, Value: %v\n", key, value)
            return true // 继续遍历
        })
    6. LoadAndDelete

      • 功能:原子地删除键并返回其值。如果键存在,返回其值和 true;如果键不存在,返回 nil 和 false
      • 用法示例:value, ok := m.LoadAndDelete(key)

sync.Map 内部使用读写锁(rwlock)来保证并发安全性,读操作可以并发进行,而写操作会独占锁。这使得 sync.Map 在高并发读操作时性能较好,但在写操作较多时可能不如使用互斥锁的 map 性能高。

package main

import (
    "fmt"
    "sync"
)

func main() {
    var m sync.Map

    // Store
    m.Store("key1", "value1")
    m.Store("key2", "value2")

    // Load
    value, ok := m.Load("key1")
    fmt.Println(value, ok) // 输出: value1 true

    // LoadOrStore
    value, loaded := m.LoadOrStore("key3", "value3")
    if !loaded {
        fmt.Println("key3 was added")
    }
    fmt.Println(value, loaded) // 输出: value3 false

    // Delete
    m.Delete("key2")

    // LoadOrDelete
    value1, loaded1 := m.LoadAndDelete("key3")
    if !loaded1 {
        fmt.Println("key3 was loaded and deleted")
    }
    fmt.Println(value1, loaded1) // 输出: value3 true

    // Range
    m.Range(func(key, value interface{}) bool {
        fmt.Printf("%v: %v\n", key, value)
        return true
    })
}

//输出结果:
//value1 true
//key3 was added
//value3 false
//value3 true
//key1: value1
PREV
[Golang] Channel原理
NEXT
[Golang] Map的底层实现原理与并发安全(待填坑)

评论(0)

发布评论