【发布时间】:2017-02-14 15:48:12
【问题描述】:
这是 Golang 的示例代码之一。但无法理解为什么在这种情况下需要“完成”频道。
https://gobyexample.com/closing-channels
没有理由将 true 发送到 done 通道。打印“已发送所有作业”消息时,我们可以知道作业通道已完成,不是吗?
我删除了与完成通道相关的代码,结果仍然相同。
【问题讨论】:
这是 Golang 的示例代码之一。但无法理解为什么在这种情况下需要“完成”频道。
https://gobyexample.com/closing-channels
没有理由将 true 发送到 done 通道。打印“已发送所有作业”消息时,我们可以知道作业通道已完成,不是吗?
我删除了与完成通道相关的代码,结果仍然相同。
【问题讨论】:
不,结果不一样:
在许多情况下(例如,不同的 CPU 负载,它是不确定的和系统相关的行为),您的主 goroutine 在您的 received job goroutine 之前退出,因此您不能保证 all jobs received,例如只需添加
time.Sleep(500)
之前
fmt.Println("received job", j)
要查看此内容,请在 The Go Playground 上尝试:
// _Closing_ a channel indicates that no more values
// will be sent on it. This can be useful to communicate
// completion to the channel's receivers.
package main
import (
"fmt"
"time"
)
// In this example we'll use a `jobs` channel to
// communicate work to be done from the `main()` goroutine
// to a worker goroutine. When we have no more jobs for
// the worker we'll `close` the `jobs` channel.
func main() {
jobs := make(chan int, 5)
//done := make(chan bool)
// Here's the worker goroutine. It repeatedly receives
// from `jobs` with `j, more := <-jobs`. In this
// special 2-value form of receive, the `more` value
// will be `false` if `jobs` has been `close`d and all
// values in the channel have already been received.
// We use this to notify on `done` when we've worked
// all our jobs.
go func() {
for {
j, more := <-jobs
if more {
time.Sleep(500)
fmt.Println("received job", j)
} else {
fmt.Println("received all jobs")
//done <- true
return
}
}
}()
// This sends 3 jobs to the worker over the `jobs`
// channel, then closes it.
for j := 1; j <= 3; j++ {
jobs <- j
fmt.Println("sent job", j)
}
close(jobs)
fmt.Println("sent all jobs")
// We await the worker using the
// [synchronization](channel-synchronization) approach
// we saw earlier.
//<-done
}
输出:
sent job 1
sent job 2
sent job 3
sent all jobs
代替:
sent job 1
received job 1
received job 2
sent job 2
sent job 3
received job 3
received all jobs
sent all jobs
见:
Goroutine does not execute if time.Sleep included
Why is time.sleep required to run certain goroutines?
Weird channel behavior in go
【讨论】:
TL;DR:有一个竞争条件---你很幸运。
如果您没有done 频道,则程序的输出是不确定的。
根据线程执行顺序,主线程可能会在 goroutine 完成处理之前退出,从而导致 goroutine 在中途被杀死。
通过强制主线程从done 通道读取数据,我们强制主线程等待,直到done 通道中有一些数据要消耗。这为我们提供了一种简洁的同步机制,其中 goroutine 通过写入done 通道来通知主线程它已完成。这反过来会导致主线程的阻塞<- done 完成并导致程序终止。
【讨论】:
我认为接受的答案没有详细说明原因。
go 语言属于过程范式,意味着每条指令都是线性执行的。当一个 go 例程从主 go 例程派生出来时,它会开始自己的小冒险,让主线程返回。
缓冲通道的容量为 5,这意味着它不会阻塞,直到缓冲区已满。如果它是空的,它也会阻塞(容量为零的通道本质上是无缓冲的)。
由于只有4次迭代(0到
通过指示主线程从完成通道读取数据,我们强制主线程等待,直到完成通道中有一些数据要消耗。当迭代结束时,执行else分支,写操作done <- true导致主线程释放<- done读操作。读取操作等待从done 中提取现在插入的值。
从done 读取后,Go 主程序不再被阻塞,因此成功终止。
【讨论】:
发送并不意味着工作已经完成,当工作需要很长时间才能完成时 结束。
作业通道被缓冲,因此即使发送作业,作业也可能 甚至还没有被工人收到。
【讨论】: