» Go语言快速入门 » 3. 高级篇 » 3.4 Sync 同步

同步

sync 包提供了基础的同步原语,如互斥锁。除了 OnceWaitGroup 类型外,大多数都是供给底层库使用的。高层的同步最好通过通道通信完成。

Once

sync.Once 类型确保一个函数只被执行一次,无论有多少个 goroutine 调用它。这对于希望执行一些初始化或设置的场景非常有用。

package main

import (
	"fmt"
	"sync"
  "time"
)

var (
	initialized bool
	initOnce    sync.Once
)

func initialize() {
	fmt.Println("Initializing...")
	// 在这里执行初始化任务
	initialized = true
}

func performTask() {
	// sync.Once.Do 传递的函数只会被执行一次,
	// 即使有多个 goroutine 调用它。
	initOnce.Do(initialize)

	// 在这里执行实际任务
	fmt.Println("Performing the task...")
}

func main() {
	// 在多个 goroutine 中运行 performTask
	for i := 0; i < 5; i++ {
		go performTask()
	}

	fmt.Println("Waiting for tasks to complete...")
	time.Sleep(2 * time.Second)
}
// =>
// Waiting for tasks to complete...
// Initializing...
// Performing the task...
// Performing the task...
// Performing the task...
// Performing the task...
// Performing the task...

WaitGroups

sync.WaitGroup 用于等待一组 goroutine 完成它们的执行。

package main

import (
	"fmt"
	"sync"
	"time"
)

func worker(id int, wg *sync.WaitGroup) {
	defer wg.Done() // 当 goroutine 完成时减少计数器

	fmt.Printf("Worker %d started\n", id)
	time.Sleep(time.Second) // 模拟一些工作
	fmt.Printf("Worker %d completed\n", id)
}

func main() {
	var wg sync.WaitGroup

	numWorkers := 3

	for i := 1; i <= numWorkers; i++ {
		wg.Add(1) // 在启动新 goroutine 之前递增计数器
		go worker(i, &wg)
	}

	// 等待所有 goroutine 完成
	wg.Wait()

	fmt.Println("All workers have completed.")
}
// =>
// Worker 3 started
// Worker 1 started
// Worker 2 started
// Worker 3 completed
// Worker 2 completed
// Worker 1 completed
// All workers have completed.

原子计数器

sync/atomic 包提供了基本类型的原子操作,包括原子计数器。 原子操作对于在并发程序中无锁管理共享状态是至关重要的。

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

func incrementCounter(counter *int64, wg *sync.WaitGroup) {
	defer wg.Done()

	for i := 0; i < 10000; i++ {
		// 原子地增加计数器
		atomic.AddInt64(counter, 1)
	}

	time.Sleep(5 * time.Millisecond)
}

func main() {
	var counter int64

	var wg sync.WaitGroup

	numGoroutines := 5

	for i := 0; i < numGoroutines; i++ {
		wg.Add(1)
		go incrementCounter(&counter, &wg)
	}

	wg.Wait()

	fmt.Println("Final Counter Value:", atomic.LoadInt64(&counter))
}
// => Final Counter Value: 50000

互斥锁

互斥锁(mutex, 即 mutual exclusion)是一种同步原语,用于保护共享数据,防止多个 goroutine 同时访问时引发冲突。

package main

import (
	"fmt"
	"sync"
)

type Counter struct {
	value int
	mu    sync.Mutex
}

func (c *Counter) Increment() {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.value++
}

func (c *Counter) GetValue() int {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.value
}

func main() {
	var wg sync.WaitGroup

	counter := Counter{}

	numGoroutines := 5

	for i := 0; i < numGoroutines; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			for j := 0; j < 10000; j++ {
				counter.Increment()
			}
		}()
	}

	wg.Wait()

	fmt.Println("Final Counter Value:", counter.GetValue())
}
// => Final Counter Value: 50000

互斥锁适用于保护复杂的共享资源、临界区或需要对锁进行精细控制的场景。而原子计数器适用于简单场景,比如共享的资源是个简单计数器或标志的情况。

代码挑战

使用互斥锁实现一个可以被多个 goroutine 安全地递增和递减的并发计数器。

Loading...
> 此处输出代码运行结果
上页
下页