在Go中,sync.Mutext
或chan
用于防止并发访问共享对象。但是,在某些情况下,我只是对变量或对象字段的最新值感兴趣。还是我喜欢写一个值,不在乎另一个go例程以后是否覆盖它,还是以前才覆盖它。
更新:
TLDR;只是不要这样做。这不安全。阅读答案,评论和链接的文档!这里是示例程序的两个变体good
和bad
,它们似乎都使用当前的Go运行时产生“正确的”输出:
package main import ( "flag" "fmt" "math/rand" "time" ) var bogus = flag.Bool("bogus", false, "use bogus code") func pause() { time.Sleep(time.Duration(rand.Uint32()%100) * time.Millisecond) } func bad() { stop := time.After(100 * time.Millisecond) var name string // start some producers doing concurrent writes (DANGER!) for i := 0; i < 10; i++ { go func(i int) { pause() name = fmt.Sprintf("name = %d", i) }(i) } // start consumer that shows the current value every 10ms go func() { tick := time.Tick(10 * time.Millisecond) for { select { case <-stop: return case <-tick: fmt.Println("read:", name) } } }() <-stop } func good() { stop := time.After(100 * time.Millisecond) names := make(chan string, 10) // start some producers concurrently writing to a channel (GOOD!) for i := 0; i < 10; i++ { go func(i int) { pause() names <- fmt.Sprintf("name = %d", i) }(i) } // start consumer that shows the current value every 10ms go func() { tick := time.Tick(10 * time.Millisecond) var name string for { select { case name = <-names: case <-stop: return case <-tick: fmt.Println("read:", name) } } }() <-stop } func main() { flag.Parse() if *bogus { bad() } else { good() } }
预期输出如下:
... read: name = 3 read: name = 3 read: name = 5 read: name = 4 ...
read:
和read: name=[0-9]
的任何组合是此程序的正确输出。接收任何其他字符串作为输出将是错误。
[使用go run --race bogus.go
运行该程序是安全的。
但是,go run --race bogus.go -bogus
会警告并发读写。
对于map
类型,并且在附加到切片时,我始终需要使用互斥锁或类似的保护方法,以避免出现段错误或意外行为。但是,为了安全起见,将字面量(原子值)读写到变量或字段值似乎
问题:
我可以安全地读取和安全地同时写入哪些Go数据类型,而无需使用mutext,不产生段错误以及不从内存中读取垃圾?请解释
为什么答案中的内容[。Update
:我重写了该示例以更好地反映原始代码,其中我遇到了并发写入问题。重要的观点已经在评论中。我将接受一个答案,该答案以足够详细的方式总结了这些学习内容(尤其是在Go运行时中)。在Go中,使用sync.Mutext或chan阻止并发访问共享对象。但是,在某些情况下,我只是对变量或对象字段的最新值感兴趣。还是我喜欢...但是,在某些情况下,我只是对变量或对象字段的最新值感兴趣。
您为什么不能使用频道
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup // wait group to close channel var buffer int = 1 // buffer of the channel // channel to get the share data cName := make(chan string, buffer) for i := 0; i < 10; i++ { wg.Add(1) // add to wait group go func(i int) { cName <- fmt.Sprintf("name = %d", i) wg.Done() // decrease wait group. }(i) } go func() { wg.Wait() // wait of wait group to be 0 close(cName) // close the channel }() // process all the data for n := range cName { println("read:", n) } }