内存同步(Memory Synchronization)

内存同步是并发编程中的一个关键概念,用于确保多个线程或Goroutine对共享数据的访问和修改是可见且一致的。在多核处理器和多级缓存的环境中,线程之间的操作顺序可能会被重排序,因此需要使用同步原语(如锁、原子操作等)来保证正确的内存可见性。

Go中的内存同步

在Go语言中,内存同步可以通过以下几种方式实现:

  1. 互斥锁(Mutex):确保在同一时间只有一个Goroutine访问临界区。
  2. 读写锁(RWMutex):允许多个读操作并发执行,但写操作是独占的。
  3. 原子操作:提供低级别的同步原语,用于对单个变量进行原子操作。
  4. Channel:Go的内置并发原语,用于在Goroutine之间传递数据和同步。

互斥锁的例子

互斥锁确保在同一时间只有一个Goroutine访问共享数据。

package main

import (
	"fmt"
	"sync"
)

var (
	counter int
	mu      sync.Mutex
)

func increment(wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 0; i < 1000; i++ {
		mu.Lock()
		counter++
		mu.Unlock()
	}
}

func main() {
	var wg sync.WaitGroup

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go increment(&wg)
	}

	wg.Wait()
	fmt.Println("Final Counter:", counter)
}

读写锁的例子

读写锁允许多个读操作并发执行,但写操作是独占的。

package main

import (
	"fmt"
	"sync"
)

var (
	counter int
	rwMutex sync.RWMutex
)

func readCounter(id int, wg *sync.WaitGroup) {
	defer wg.Done()
	rwMutex.RLock()
	defer rwMutex.RUnlock()
	fmt.Printf("Goroutine %d reading counter: %d\n", id, counter)
}

func writeCounter(id int, wg *sync.WaitGroup) {
	defer wg.Done()
	rwMutex.Lock()
	defer rwMutex.Unlock()
	counter++
	fmt.Printf("Goroutine %d writing counter: %d\n", id, counter)
}

func main() {
	var wg sync.WaitGroup

	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go readCounter(i, &wg)
	}

	for i := 1; i <= 2; i++ {
		wg.Add(1)
		go writeCounter(i, &wg)
	}

	wg.Wait()
	fmt.Println("Final Counter:", counter)
}

原子操作的例子

原子操作用于对单个变量进行原子操作,避免使用锁的开销。

package main

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

var counter int32

func increment(wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 0; i < 1000; i++ {
		atomic.AddInt32(&counter, 1)
	}
}

func main() {
	var wg sync.WaitGroup

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go increment(&wg)
	}

	wg.Wait()
	fmt.Println("Final Counter:", counter)
}

Channel的例子

Channel用于在Goroutine之间传递数据和同步。

package main

import (
	"fmt"
	"sync"
)

func increment(ch chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 0; i < 1000; i++ {
		ch <- 1
	}
}

func counter(ch chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	counter := 0
	for i := 0; i < 10000; i++ {
		counter += <-ch
	}
	fmt.Println("Final Counter:", counter)
}

func main() {
	var wg sync.WaitGroup
	ch := make(chan int)

	wg.Add(1)
	go counter(ch, &wg)

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go increment(ch, &wg)
	}

	wg.Wait()
}

小结

内存同步在并发编程中至关重要,用于确保多个线程或Goroutine对共享数据的访问和修改是可见且一致的。Go语言提供了多种内存同步机制,包括互斥锁、读写锁、原子操作和Channel,可以根据具体需求选择合适的同步方式。通过这些同步原语,可以编写出安全、可靠的并发程序。