返回

超越传统:Go Sync 包的六大秘籍,并发编程从此不纠结!

后端

超越传统:Go Sync 包的六大秘籍,并发编程从此不纠结!

并发编程是现代软件开发中不可或缺的一部分,但它同时也充满了挑战。在 Go 语言中,sync 包为我们提供了丰富的并发编程工具,让我们能够轻松应对并发编程中的各种难题。本文将深入探讨 sync 包中的六个关键概念:Mutex、RWMutex、WaitGroup、Cond、Once 和 Atomic,并辅以代码示例,让您全面理解其运作机制。掌握这些概念,您将如鱼得水,纵横并发编程之海。

一、Mutex:互斥锁的利剑,守护共享资源的安全

Mutex,即互斥锁,是并发编程中的基本工具,用于保护共享资源不被多个 goroutine 同时访问。它就像一把利剑,在共享资源的入口处站岗放哨,确保只有一个 goroutine 能够进入。

import (
	"fmt"
	"sync"
)

var (
	mu    sync.Mutex
	count int
)

func increment() {
	mu.Lock()
	count++
	mu.Unlock()
}

func main() {
	for i := 0; i < 100; i++ {
		go increment()
	}
	fmt.Println(count)
}

二、RWMutex:读写锁的妙笔,平衡共享资源的读写

RWMutex,即读写锁,是 Mutex 的升级版,它允许多个 goroutine 同时读取共享资源,但只能有一个 goroutine 写入共享资源。这就像一把妙笔,在共享资源的读写之间取得了微妙的平衡。

import (
	"fmt"
	"sync"
)

var (
	rmu   sync.RWMutex
	count int
)

func read() {
	rmu.RLock()
	fmt.Println(count)
	rmu.RUnlock()
}

func write() {
	rmu.Lock()
	count++
	rmu.Unlock()
}

func main() {
	for i := 0; i < 100; i++ {
		go read()
	}
	for i := 0; i < 10; i++ {
		go write()
	}
	fmt.Println(count)
}

三、WaitGroup:等待协程的号角,协调协程的同步

WaitGroup,即等待组,用于协调多个 goroutine 的同步。它就像一个号角,当所有 goroutine 都执行完毕时,它会发出信号,通知等待的 goroutine 继续执行。

import (
	"fmt"
	"sync"
)

var (
	wg sync.WaitGroup
)

func hello(i int) {
	defer wg.Done()
	fmt.Println("Hello", i)
}

func main() {
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go hello(i)
	}
	wg.Wait()
	fmt.Println("All goroutines finished!")
}

四、Cond:条件变量的哨兵,唤醒沉睡的协程

Cond,即条件变量,用于在满足某些条件时唤醒正在等待的 goroutine。它就像一个哨兵,当条件满足时,它会发出信号,唤醒沉睡的 goroutine,让它们继续执行。

import (
	"fmt"
	"sync"
)

var (
	mu  sync.Mutex
	cond sync.Cond
	data = make([]int, 0)
)

func producer() {
	for i := 0; i < 10; i++ {
		mu.Lock()
		data = append(data, i)
		mu.Unlock()
		cond.Signal()
	}
}

func consumer() {
	mu.Lock()
	for len(data) == 0 {
		cond.Wait(&mu)
	}
	fmt.Println(data[0])
	data = data[1:]
	mu.Unlock()
}

func main() {
	go producer()
	go consumer()
	var input string
	fmt.Scanln(&input)
}

五、Once:一次性的执行,确保任务的唯一性

Once,即一次性执行,用于确保某个任务只执行一次。它就像一个开关,一旦被打开,就无法再关闭。这对于需要确保任务只执行一次的场景非常有用。

import (
	"fmt"
	"sync"
)

var (
	once sync.Once
	initialized = false
)

func initOnce() {
	once.Do(func() {
		initialized = true
	})
}

func main() {
	for i := 0; i < 10; i++ {
		go initOnce()
	}
	fmt.Println(initialized)
}

六、Atomic:原子操作的利器,保障数据的安全

Atomic,即原子操作,用于确保多个 goroutine 对共享变量的修改是原子的,即不会被中断。它就像一把利器,能够保障数据的安全。

import (
	"fmt"
	"sync/atomic"
)

var count int32

func increment() {
	atomic.AddInt32(&count, 1)
}

func main() {
	for i := 0; i < 100; i++ {
		go increment()
	}
	fmt.Println(count)
}