Go 语言入门很简单:并发同步的几种方式

152 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 28 天,点击查看活动详情

Go 语言同步的方式

Golang Sync 包提供了同步原语,例如:

  • mutex
  • Waitgroup
  • Pool
  • Read
  • Write mutex
  • condition variables条件变量

sync.Mutex 提供了一个互斥原语,它允许共享资源的互斥,防止竞争条件。

package main

import (
	"sync"
)

func main() {

	i := 10
	mutex := &sync.Mutex{}
	mutex.Lock()
	i++
	mutex.Unlock()
}

读写锁

sync.RWMutex 提供读写锁,它提供了与原始互斥锁类似的方法,但可以使用 RLockRUnlock 方法允许并发读取:

package main

import (
	"sync"
)

func main() {

	i := 10
	mutex := &sync.RWMutex{}
	mutex.Lock()
	// only one goroutine can access this code at a time
	i++
	mutex.Unlock()

	mutex.RUnlock()
	i++ // concurrent reads
	mutex.RUnlock()
}

上一个示例可以允许多个 goroutine 读取代码。与 sync.Mutex 不同,它一次只允许一个读取器和一个写入器。

Waitgroups

sync.Waitgroup 用于为 goroutine 提供阻塞机制。使用 Waitgroup,您可以阻止函数的执行,直到所有 goroutine 执行完毕。

它通过创建一个计数器来工作,该计数器保存要等待的 goroutine 的数量。一旦一个 goroutine 完成,计数器减 1。一旦计数器为 0,Waitgroup 就会解除对执行的阻塞。

要将值添加到 Waitgroup 计数器,我们可以使用 Add() 方法,该方法采用整数值。

要在完成后从计数器中删除一个 goroutine,我们使用 Done() 方法。例如:

package main

import (
	"fmt"
	"sync"
)

func main() {
	wg := &sync.WaitGroup{}
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(x int) {
			fmt.Printf("Worker: %d running\n", x)
			wg.Done()
		}(i)
	}
	wg.Wait()
}

在前面的示例中,我们通过使用 Add() 函数将 Waitgroup 计数器的值加 1。

一旦一个 goroutine 完成,我们使用 Done() 方法将计数器减 1。前面的代码应返回输出为:

Worker: 3 running
Worker: 1 running
Worker: 4 running
Worker: 0 running
Worker: 2 running

Once

sync.Once 原语确保一个函数只运行一次。一个例子如下所示:

package main

import (
	"fmt"
	"sync"
)

func main() {
	var once sync.Once
	RunOnce := func() {
		fmt.Println("Run once")
	}
	done := make(chan string)
	for i := 0; i < 5; i++ {
		go func() {
			once.Do(RunOnce)
			done <- "Hi"
		}()
	}
	for i := 0; i < 5; i++ {
		<-done
	}
}

运行该代码:

Run once

总结

本文涵盖了 Go sync 包提供的一些基本方法:如互斥锁、读写锁、等待组、Once 等同步原语的用法。更多原理和使用方法等待读者去探索。