锁定不会阻止映射覆盖值。

huangapple go评论95阅读模式
英文:

Locking not preventing map to overwrite values

问题

在这段代码中,我试图输入一个唯一的键来进行映射,我实现了一个MySafeMap来确保Get/Set是受到读写保护的。尽管当我执行程序时,我看到许多workers最终被插入到映射中。我期望只有一个唯一的条目被插入到映射中,其他的workers则重用它。

package main

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

type M struct {
	c  chan string
	ms *MySafeMap
}

type MySafeMap struct {
	sync.RWMutex
	m map[string]any
}

func (w *MySafeMap) Set(key string, value any) {
	w.Lock()
	defer w.Unlock()
	w.m[key] = value
}

func (w *MySafeMap) Get(key string) (any, bool) {
	w.RLock()
	defer w.RUnlock()
	v, ok := w.m[key]
	return v, ok
}

func (m *M) worker(ii int) {
	for {
		v := <-m.c
		if _, ok := m.ms.Get(v); !ok {
			log.Println("entry not found, created", ii)
			m.ms.Set(v, nil)
		}
	}
}

func main() {
	m := M{
		c:  make(chan string, 100),
		ms: &MySafeMap{m: make(map[string]any)},
	}
	for ii := 0; ii < 100; ii++ {
		go m.worker(ii)
	}
	time.Sleep(time.Second)
	for ii := 0; ii < 100; ii++ {
		m.c <- "10"
		time.Sleep((time.Microsecond))
	}
	fmt.Println("Hello World")
}

输出结果为:

➜  Downloads go run ./prog.go
2023/03/01 20:46:36 entry not found, created 1
2023/03/01 20:46:36 entry not found, created 6
2023/03/01 20:46:36 entry not found, created 4
2023/03/01 20:46:36 entry not found, created 5
2023/03/01 20:46:36 entry not found, created 8
2023/03/01 20:46:36 entry not found, created 7
2023/03/01 20:46:36 entry not found, created 0
2023/03/01 20:46:36 entry not found, created 2
2023/03/01 20:46:36 entry not found, created 9
2023/03/01 20:46:36 entry not found, created 3
2023/03/01 20:46:36 entry not found, created 10
2023/03/01 20:46:36 entry not found, created 14
2023/03/01 20:46:36 entry not found, created 11
2023/03/01 20:46:36 entry not found, created 12
Hello World
英文:

In this code, I am trying to enter a unique key to map, I have implemented a MySafeMap to ensure Get/Set are RW protected. Although when I execute the program, I see many of the workers end up inserted into the map. I am expecting only a unique entry to be inserted into the map and other workers to reuse it.

package main
import (
&quot;fmt&quot;
&quot;log&quot;
&quot;sync&quot;
&quot;time&quot;
)
type M struct {
c  chan string
ms *MySafeMap
}
type MySafeMap struct {
sync.RWMutex
m map[string]any
}
func (w *MySafeMap) Set(key string, value any) {
w.Lock()
defer w.Unlock()
w.m[key] = value
}
func (w *MySafeMap) Get(key string) (any, bool) {
w.RLock()
defer w.RUnlock()
v, ok := w.m[key]
return v, ok
}
func (m *M) worker(ii int) {
for {
v := &lt;-m.c
if _, ok := m.ms.Get(v); !ok {
log.Println(&quot;entry not found, created&quot;, ii)
m.ms.Set(v, nil)
}
}
}
func main() {
m := M{
c:  make(chan string, 100),
ms: &amp;MySafeMap{m: make(map[string]any)},
}
for ii := 0; ii &lt; 100; ii++ {
go m.worker(ii)
}
time.Sleep(time.Second)
for ii := 0; ii &lt; 100; ii++ {
m.c &lt;- &quot;10&quot;
time.Sleep((time.Microsecond))
}
fmt.Println(&quot;Hello World&quot;)
}

and the output is

➜  Downloads go run ./prog.go
2023/03/01 20:46:36 entry not found, created 1
2023/03/01 20:46:36 entry not found, created 6
2023/03/01 20:46:36 entry not found, created 4
2023/03/01 20:46:36 entry not found, created 5
2023/03/01 20:46:36 entry not found, created 8
2023/03/01 20:46:36 entry not found, created 7
2023/03/01 20:46:36 entry not found, created 0
2023/03/01 20:46:36 entry not found, created 2
2023/03/01 20:46:36 entry not found, created 9
2023/03/01 20:46:36 entry not found, created 3
2023/03/01 20:46:36 entry not found, created 10
2023/03/01 20:46:36 entry not found, created 14
2023/03/01 20:46:36 entry not found, created 11
2023/03/01 20:46:36 entry not found, created 12
Hello World

答案1

得分: 3

你在更新地图时使用了内存竞争保护,但仍然存在竞态条件。一般来说,任何类型的if condition then update语句都容易出现竞态条件。不能保证多个goroutine会同时运行检查,决定条件成立并执行更新操作。你需要在整个操作期间保护并发访问:

var updateMutex sync.Mutex

func (m *M) worker(ii int) {
    for {
        v := <-m.c
        updateMutex.Lock()
        if _, ok := m.ms.Get(v); !ok {
            log.Println("entry not found, created", ii)
            m.ms.Set(v, nil)
        }
        updateMutex.Unlock()
    }
}
英文:

You protected from memory race updating the map, but you still have a race condition. In general, any type of if condition then update statements are race prone. There are no guarantees that multiple goroutines will run the check, decide that the condition holds, and performs the update. You need to protect concurrent access during the whole operation:

var updateMutex sync.Mutex
func (m *M) worker(ii int) {
for {
v := &lt;-m.c
updateMutex.Lock()
if _, ok := m.ms.Get(v); !ok {
log.Println(&quot;entry not found, created&quot;, ii)
m.ms.Set(v, nil)
}
updateMutex.Unlock()
}
}

答案2

得分: 1

程序开始时,地图中没有v,rwmutex允许许多goroutine进入Get()并返回false,然后重置v。

  • 您可以使用双重检查来防止重置地图
func (m *M) worker(ii int) {
	for {
		v := <-m.c
		if _, ok := m.ms.Get(v); !ok {
			log.Println("未找到条目,尝试创建", ii)
			// 双重检查
			updateMutex.Lock()
			if _, ok := m.ms.Get(v); !ok {
				print("已创建!\n")
				m.ms.Set(v, nil)
			}
			updateMutex.Unlock()
		}
	}
}

输出:

2023/03/08 10:40:33 未找到条目尝试创建 2
已创建
2023/03/08 10:40:33 未找到条目尝试创建 3
2023/03/08 10:40:33 未找到条目尝试创建 4
你好世界

尝试多次设置,只有一个设置成功。

英文:

At the begin of program,there is no v in map,and rwmutex let many gorouting get into Get() and return false ,then reset v.

  • you kan use double check to prevent reset the map
func (m *M) worker(ii int) {
	for {
		v := &lt;-m.c
		if _, ok := m.ms.Get(v); !ok {
			log.Println(&quot;entry not found,try created&quot;, ii)
			// double check
			updateMutex.Lock()
			if _, ok := m.ms.Get(v); !ok {
				print(&quot;created!\n&quot;)
				m.ms.Set(v, nil)
			}
			updateMutex.Unlock()
		}
	}
}

output:

2023/03/08 10:40:33 entry not found,try created 2
created!
2023/03/08 10:40:33 entry not found,try created 3
2023/03/08 10:40:33 entry not found,try created 4
Hello World

try many times to set,and only one set it.

huangapple
  • 本文由 发表于 2023年3月1日 23:18:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75605675.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定