修改消费者正在阅读的频道是否可以线程安全?
请考虑以下代码:
func main(){
channel := make(chan int, 3)
channel_ptr := &channel
go supplier (channel_ptr)
go consumer (channel_ptr)
temp = *channel_ptr
// Important bit
*channel_ptr = make(chan int, 5)
more := true
for more{
select {
case msg := <-temp:
*channel_ptr <- msg
default:
more = false
}
}
// Block main indefinitely to keep the children alive
<-make(chan bool)
}
func consumer(c *chan int){
for true{
fmt.Println(<-(*c))
}
}
func supplier(c *chan int){
for i :=0; i < 5; i ++{
(*c)<-i
}
}
如果频道和make
按我希望的方式工作,我应该获得以下属性:
Important bit
的部分是原子的)从几次测试开始,这似乎都是正确的,但我无法在文档中找到它,我担心细微的竞争条件。
是的,我在做什么都行不通。此线程可能在此时被隐藏,但有人知道如何动态调整缓冲通道的大小吗?
答案 0 :(得分:1)
它不是线程安全的。
如果您使用-race
标记运行race detector,则会看到错误:
$ run -race t.go
==================
WARNING: DATA RACE
Write at 0x00c420086018 by main goroutine:
main.main()
/Users/kjk/src/go/src/github.com/kjk/go-cookbook/start-mysql-in-docker-go/t.go:14 +0x128
Previous read at 0x00c420086018 by goroutine 6:
main.supplier()
/Users/kjk/src/go/src/github.com/kjk/go-cookbook/start-mysql-in-docker-go/t.go:37 +0x51
Goroutine 6 (running) created at:
main.main()
/Users/kjk/src/go/src/github.com/kjk/go-cookbook/start-mysql-in-docker-go/t.go:9 +0xb4
0
==================
1
2
3
==================
WARNING: DATA RACE
Read at 0x00c420086018 by goroutine 6:
main.supplier()
/Users/kjk/src/go/src/github.com/kjk/go-cookbook/start-mysql-in-docker-go/t.go:37 +0x51
Previous write at 0x00c420086018 by main goroutine:
main.main()
/Users/kjk/src/go/src/github.com/kjk/go-cookbook/start-mysql-in-docker-go/t.go:14 +0x128
Goroutine 6 (running) created at:
main.main()
/Users/kjk/src/go/src/github.com/kjk/go-cookbook/start-mysql-in-docker-go/t.go:9 +0xb4
==================
4
根据经验,您不应该将频道作为指针传递。频道已经是内部指针。
退后一步:我不明白你想要实现的目标。
我猜你有一个原因是你试图将频道作为指针传递。在Go中使用频道的模式是:您创建一次并将其作为值传递。你没有传递指针,你永远不会在创建后修改它。
在你的例子中,问题是你有一块共享的内存(channel_ptr
指向的内存地址),你在一个线程中写入该内存,而另一个线程读取它。那次数据竞赛。
它不是特定于某个频道,如果它是指向int的指针而两个线程正在修改int的值,则会遇到同样的问题。