Tob*_*bia 7 synchronization channel go goroutine
Effective Go给出了关于如何使用通道模拟信号量的示例:
var sem = make(chan int, MaxOutstanding)
func handle(r *Request) {
<-sem
process(r)
sem <- 1
}
func init() {
for i := 0; i < MaxOutstanding; i++ {
sem <- 1
}
}
func Serve(queue chan *Request) {
for {
req := <-queue
go handle(req)
}
}
Run Code Online (Sandbox Code Playgroud)
它还说:由于数据同步发生在来自通道的接收上(即发送"发生在接收之前;请参阅Go Memory模型),信号量的获取必须在通道接收上,而不是发送.
现在,我想我理解Go Memory Model和"之前发生过"的定义.但我没有看到阻止频道发送的问题:
func handle(r *Request) {
sem <- 1
process(r)
<-sem
}
func init() {}
Run Code Online (Sandbox Code Playgroud)
此代码(具有sem和Serve从上述不变)使用以相反的方式被缓冲的信道.频道开始为空.在进入时handle,如果已经有MaxOutstandinggoroutines正在执行该过程,则发送将被阻止.只要其中一个完成其处理并从通道"释放"一个槽,通过接收一个int,我们的发送将被解除阻塞,goroutine将开始自己的处理.
为什么这是一种不好的同步方式,因为教科书似乎意味着什么?
是否接收通道,其将释放通道插槽操作不将使用相同的插槽发送"之前发生"?这怎么可能?
换句话说,语言参考说"在缓冲通道上发送[阻塞直到]缓冲区中有空间."
但是内存模型只表示"来自无缓冲通道的接收发生在该通道上的发送完成之前".特别是,它并未说明在该通道上的发送完成之前,来自已满的缓冲通道的接收发生.
这个角落的情况是不是可以信赖做正确的事吗?(这实际上是同步一个被阻止的发送与取消阻止它的接收)
如果是这种情况,它看起来像一种令人讨厌的竞争条件,旨在最大限度地减少鬼鬼祟祟的竞争条件:-(
var c = make(chan int, 1)
var a string
func f() {
a = "hello, world"
<-c // unblock main, which will hopefully see the updated 'a'
}
func main() {
c <- 0 // fill up the buffered channel
go f()
c <- 0 // this blocks because the channel is full
print(a)
}
Run Code Online (Sandbox Code Playgroud)
小智 5
这篇Effective Go文档也让我感动.实际上,在相对最新版本的Effective Go中,所讨论的代码在通道发送上获取了信号量(而不是像当前版本中那样使用init()来"填充"通道).
显然有很多关于这个主题的讨论.我不打算总结一切,但讨论都可以从这里找到:
https://code.google.com/p/go/issues/detail?id=5023
它确实令我感到不幸,但引用该问题的报道者,短篇小说似乎是除非信号量是在频道上获得的......:
以下代码:
func handle(r *Request) {
sem <- 1 // Wait for active queue to drain.
process(r) // May take a long time.
<-sem // Done; enable next request to run.
}
Run Code Online (Sandbox Code Playgroud)
......可以合法地"优化"成:
func handle(r *Request) {
process(r) // May take a long time.
sem <- 1 // Wait for active queue to drain.
<-sem // Done; enable next request to run.
}
Run Code Online (Sandbox Code Playgroud)
......或者:
func handle(r *Request) {
sem <- 1 // Wait for active queue to drain.
<-sem // Done; enable next request to run.
process(r) // May take a long time.
}
Run Code Online (Sandbox Code Playgroud)