Go并发编程(2) | 青训营笔记

78 阅读2分钟

4. sync

sync 包提供了基本的同步原语,如互斥锁。除了 Once 和 WaitGroup 之外,大部分都是适用于低水平程序线程的,高水平的同步使用 channel 通常更好。

4.1 Mutex

Mutex 是互斥锁,Lock() 加锁,Unlock() 解锁。如果在 Lock() 之后没有对 Mutex 解锁,那么之后的操作都会被阻塞。

var mu sync.Mutex

func main() {
    mu.Lock()
    defer mu.Unlock()
}

4.2 RWMutex

RWMutex 是读写锁,分别有 Lock()、Unlock()、RLock()、RUnlock() 四个方法。R 是读,W 是写,加了 R 的锁可以进行读操作,加了 W 的锁可以进行写操作,但是加了 W 的锁后,其他 goroutine 既不能进行读操作也不能进行写操作。

var mu sync.RWMutex

func main() {
    mu.RLock()
    defer mu.RUnlock()
}

4.3 WaitGroup

WaitGroup 用于等待一组 goroutine 结束。程序控制会一直阻塞,直到这些指定的 goroutine 全部结束。

var wg sync.WaitGroup

func main() {
    wg.Add(2)
    go func() {
        fmt.Println("Goroutine 1")
        wg.Done()
    }()
    go func() {
        fmt.Println("Goroutine 2")
        wg.Done()
    }()
    wg.Wait()
}

4.4 Once

Once 用于多次调用同一个函数,但只有第一次调用时才执行函数。

var once sync.Once

func main() {
    once.Do(func() {
        fmt.Println("Only once")
    })
}

4.5 Pool

Pool 用于存储临时对象,它可以安全地被多个 goroutine 并发使用。

var pool = sync.Pool{
    New: func() interface{} {
        return 0
    },
}

func main() {
    pool.Put(1)
    pool.Put(2)
    pool.Put(3)
    fmt.Println(pool.Get())
    fmt.Println(pool.Get())
    fmt.Println(pool.Get())
}

5. context

context 包提供了一个用于跟踪请求的上下文(context)。在 Go 语言中,每个请求都是在独立的 goroutine 中处理的,这就使得我们可以很容易地对每个请求进行并发控制。但是,当我们需要跨越多个 goroutine 时,就需要一种跟踪上下文的机制,这就是 context 包的用武之地。

5.1 context 的声明

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}

5.2 context 的应用场景

  • 超时控制
  • 多个 goroutine 之间的数据传递
  • 多个 goroutine 之间的信号通知

6. sync.Map

sync.Map 是 Go 语言提供的内置并发安全的 map,开箱即用。不过需要注意的是,sync.Map 不能使用 map 的方式进行取值和设置等操作,而是使用 sync.Map 的方法进行调用。

6.1 sync.Map 的声明

type Map struct {
    // 包含隐藏或非导出字段
}

6.2 sync.Map 的初始化

var m = sync.Map{}

6.3 sync.Map 的操作

m.Store(key, value)    // 设置键值对
m.Load(key)            // 获取键值对
m.Delete(key)          // 删除键值对
m.Range(func(key, value interface{}) bool {
    // 遍历所有键值对
    return true
})

6.4 sync.Map 的注意事项

  • sync.Map 不能使用 map 的方式进行取值和设置等操作,而是使用 sync.Map 的方法进行调用。
  • sync.Map 无法在创建后再进行复制操作。
  • sync.Map 与其他结构不同,它不是一个结构体,因此我们在声明的时候只需要声明变量即可,不需要初始化。