【发布时间】:2022-02-26 11:47:51
【问题描述】:
我有一个服务器来处理事件,这个服务器有一个mutex lock 和一个events 表(映射结构)。当服务器接收到一个新事件时,它会获取lock 以防止数据竞争,将此事件存储在事件表中,并启动一个 goroutine 来监视此事件是否已完成。如果我使用-race 标志运行程序,它将输出data race。
package main
import (
"sync"
"time"
)
type event struct {
done chan bool
}
type server struct {
mu sync.Mutex
events map[int]*event
}
func main() {
s := server{}
s.events = make(map[int]*event)
for i := 0; i < 10; i++ {
go func(i int) {
s.mu.Lock()
s.events[i] = &event{}
s.events[i].done = make(chan bool)
s.mu.Unlock()
go func() {
time.Sleep(1 * time.Millisecond)
<-s.events[i].done
// server do something.
}()
}(i)
}
time.Sleep(1 * time.Second)
for i := 0; i < 10; i++ {
// event happen.
s.events[i].done <- true
}
}
输出
==================
WARNING: DATA RACE
Read at 0x00c00010dd10 by goroutine 14:
runtime.mapaccess1_fast64()
c:/go/src/runtime/map_fast64.go:12 +0x0
main.main.func1.1()
C:/SimpleAsyncBFT/race/main.go:29 +0x7c
Previous write at 0x00c00010dd10 by goroutine 15:
runtime.mapassign_fast64()
c:/go/src/runtime/map_fast64.go:92 +0x0
main.main.func1()
C:/SimpleAsyncBFT/race/main.go:24 +0xbe
Goroutine 14 (running) created at:
main.main.func1()
C:/SimpleAsyncBFT/race/main.go:27 +0x1c6
Goroutine 15 (finished) created at:
main.main()
C:/SimpleAsyncBFT/race/main.go:22 +0xed
我知道在monitor goroutine中添加lock可以解决这个问题,但是会导致死锁! done 通道只是用来通知服务器事件已经完成。如果通道不适合这种情况,如何实现?
【问题讨论】:
-
this answer 有帮助吗? (相关release note 声明“如果一个 goroutine 正在写入映射,则其他 goroutine 不应同时读取或写入映射”)您的
Mutex正在防止同时写入,但读取仍然可以与写入同时发生(在第 29 行读取,在第 24 行写入)。 -
@Brits 我已经阅读了这个答案。但是如果我使用
Mutex来防止同时读取,可能会导致死锁,因为读取goroutine直到接收到来自channel的数据才会释放lock...
标签: go