gpt4 book ai didi

synchronization - 在 channel 上阻塞发送一个错误的同步范例以及为什么

转载 作者:IT王子 更新时间:2023-10-29 01:14:41 25 4
gpt4 key购买 nike

Effective Go给出了这个关于如何用 channel 模拟信号量的例子:

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)
}
}

它还说:因为数据同步发生在从 channel 接收时(也就是说,发送“发生在”接收之前;参见 The Go Memory Model),信号量的获取必须在 channel 接收上,不是发送。

现在,我想我理解了 Go 内存模型和“发生在之前”的定义。但是我看不出阻塞 channel 发送有什么问题:

func handle(r *Request) {
sem <- 1
process(r)
<-sem
}

func init() {}

此代码(semServe 与上面相同)以相反的方式使用缓冲 channel 。 channel 开始是空的。在进入 handle 时,如果已经有 MaxOutstanding goroutines 执行该过程,则发送将被阻止。一旦其中一个完成处理并从 channel “释放”一个插槽,通过接收一个 int,我们的发送将被解除阻塞,goroutine 将开始自己的处理。

为什么这是一种糟糕的同步方式,正如教科书所暗示的那样?

释放 channel 插槽的接收操作是否“发生在”将使用同一插槽的发送操作之前?这怎么可能?


换句话说,Language Reference“缓冲 channel 上的发送[阻塞直到]缓冲区中有空间。”

但是Memory Model只说“从无缓冲 channel 接收发生在该 channel 上的发送完成之前。”特别是,它说从缓冲 channel 接收 channel 已满发生在该 channel 上的发送完成之前。

这是一些不能被信任做正确事情的极端案例吗? (这实际上是将被阻塞的发送与解除阻塞的接收同步)

如果是这样的话,它看起来像是一种旨在最大限度地减少偷偷摸摸的竞争条件的语言中的令人讨厌的竞争条件:-(

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)
}

最佳答案

Effective Go 文档的这一点也让我震惊。事实上,在相对较新的 Effective Go 版本中,有问题的代码在 channel 发送上获取了信号量(而不是像当前版本那样使用 init() 来“启动” channel 的 channel 接收)。

关于这个话题显然有很多讨论。我不会费心去总结一切,但讨论都可以从这里找到:

https://code.google.com/p/go/issues/detail?id=5023

这确实让我觉得很不幸,但引用那个问题的归档者的话,短篇故事似乎是除非信号量是在接收 channel 上获取的……:

以下代码:

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.
}

...可以合法地“优化”成:

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.
}

...或进入:

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.
}

关于synchronization - 在 channel 上阻塞发送一个错误的同步范例以及为什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16324798/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com