英文:
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 (
"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")
}
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 := <-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()
}
}
答案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 := <-m.c
if _, ok := m.ms.Get(v); !ok {
log.Println("entry not found,try created", ii)
// double check
updateMutex.Lock()
if _, ok := m.ms.Get(v); !ok {
print("created!\n")
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论