Golang中如何在goroutines中使用map

Golang中如何在goroutines中使用map 我正在尝试更多地理解竞态条件,因此编写了这个程序,它基本上只是递增映射中的一个值。

在goroutine中对映射进行并发访问应该使用某种同步机制,这就是为什么我在这里使用了互斥锁。

由于程序使用了WaitGroup和互斥锁进行同步,映射中的最终值应该是100,但我们得到的结果并非如此。

我是否遗漏了什么?

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

type mapData struct {
	sync.RWMutex
	data map[string]int
}

func (m *mapData) read(key string) int {
	m.RLock()
	defer m.RUnlock()
	return m.data[key]
}

func (m *mapData) write(key string) {
	m.Lock()
	m.data[key]++
	m.Unlock()
	wg.Done()
}

func main() {
	m := mapData{}
	m.data = make(map[string]int)

	for i := 1; i <= 100; i++ {
		wg.Add(1)
		go m.write("a")
	}

	fmt.Println(m.read("a"))

	wg.Wait()
}

更多关于Golang中如何在goroutines中使用map的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

说得太棒了。谢谢 😉

更多关于Golang中如何在goroutines中使用map的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


最终的答案应该是100。

最终的答案是100。你只需要耐心地 Wait

wg.Wait()
fmt.Println("final", m.read("a"))
package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

type mapData struct {
	sync.RWMutex
	data map[string]int
}

func (m *mapData) read(key string) int {
	m.RLock()
	defer m.RUnlock()
	return m.data[key]
}

func (m *mapData) write(key string) {
	m.Lock()
	m.data[key]++
	m.Unlock()
	wg.Done()
}

func main() {
	m := mapData{}
	m.data = make(map[string]int)

	for i := 1; i <= 100; i++ {
		wg.Add(1)
		go m.write("a")
	}

	fmt.Println(m.read("a"))

	wg.Wait()

	fmt.Println("final", m.read("a"))
}

Go Playground - The Go Programming Language

final 100

你的代码存在一个关键问题:fmt.Println(m.read("a"))wg.Wait() 之前执行,这意味着主 goroutine 在等待所有写操作完成之前就尝试读取映射值。这会导致竞态条件,因为读取发生时写 goroutines 可能仍在执行。

以下是修正后的代码:

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

type mapData struct {
	sync.RWMutex
	data map[string]int
}

func (m *mapData) read(key string) int {
	m.RLock()
	defer m.RUnlock()
	return m.data[key]
}

func (m *mapData) write(key string) {
	m.Lock()
	m.data[key]++
	m.Unlock()
	wg.Done()
}

func main() {
	m := mapData{}
	m.data = make(map[string]int)

	for i := 1; i <= 100; i++ {
		wg.Add(1)
		go m.write("a")
	}

	wg.Wait() // 等待所有写操作完成
	fmt.Println(m.read("a")) // 现在安全读取
}

输出:

100

另一个更简洁的版本,使用 sync.Map 处理并发映射访问:

package main

import (
	"fmt"
	"sync"
)

func main() {
	var m sync.Map
	var wg sync.WaitGroup

	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			val, _ := m.LoadOrStore("a", 0)
			m.Store("a", val.(int)+1)
		}()
	}

	wg.Wait()
	
	val, _ := m.Load("a")
	fmt.Println(val) // 100
}

sync.Map 提供了线程安全的 Load、Store、LoadOrStore 等方法,适用于键值对数量较多或键值对频繁更新的并发场景。

回到顶部