Golang - 为什么使用完成通道
Golang - Why use done channel
Golang示例代码之一。但无法理解为什么在这种情况下需要 'done' 频道。
https://gobyexample.com/closing-channels
没有理由将 true 发送到 done 频道。当打印 "sent all jobs" 消息时,我们可以知道作业通道已完成,不是吗?
我删除了相对于完成通道和结果仍然相同的代码。
TL;DR:存在竞争条件---你很幸运。
如果您没有 done
频道,则程序的输出是不确定的。
根据线程的执行顺序,主线程可能会在goroutine完成处理之前退出,从而导致goroutine在中途被杀死。
通过强制主线程从 done
通道读取,我们强制主线程等待,直到 done
通道中有一些数据被消耗。这为我们提供了一个简洁的同步机制,其中 goroutine 通过写入 done
通道通知主线程它已完成。这反过来导致主线程的阻塞 <- done
完成并导致程序终止。
发送不代表任务完成,当任务需要很长时间才能完成
结束。
作业通道是缓冲的,所以即使作业被发送,作业也可能
工人还没有收到。
不,结果不一样:
在许多情况下,您的主 goroutine 在 received job
goroutine 之前退出(例如,不同的 CPU 负载,并且它是不确定的和系统相关的行为),因此您无法保证 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
参见:
Why is time.sleep required to run certain goroutines?
我认为已接受的答案没有详细说明原因。
go语言属于过程范式,即每条指令都是线性执行的。当一个 go 例程从主 go 例程派生出来时,它会开始自己的小冒险,将主线程留给 return.
buffered channel 的容量为5,这意味着在缓冲区满之前不会阻塞。如果它是空的,它也会阻塞(容量为零的通道本质上是无缓冲的)。
由于只有4次迭代(0到<=3),读取操作不会阻塞。
通过指示主线程从 done 通道读取,我们强制主线程等待,直到 done 通道中有一些数据要使用。迭代结束后执行else分支,写操作done <- true
导致主线程中的<- done
读操作释放。读取操作等待从 done
.
中提取现在插入的值
从 done
读取后,Go 主程序不再阻塞,因此成功终止。
Golang示例代码之一。但无法理解为什么在这种情况下需要 'done' 频道。
https://gobyexample.com/closing-channels
没有理由将 true 发送到 done 频道。当打印 "sent all jobs" 消息时,我们可以知道作业通道已完成,不是吗?
我删除了相对于完成通道和结果仍然相同的代码。
TL;DR:存在竞争条件---你很幸运。
如果您没有 done
频道,则程序的输出是不确定的。
根据线程的执行顺序,主线程可能会在goroutine完成处理之前退出,从而导致goroutine在中途被杀死。
通过强制主线程从 done
通道读取,我们强制主线程等待,直到 done
通道中有一些数据被消耗。这为我们提供了一个简洁的同步机制,其中 goroutine 通过写入 done
通道通知主线程它已完成。这反过来导致主线程的阻塞 <- done
完成并导致程序终止。
发送不代表任务完成,当任务需要很长时间才能完成 结束。
作业通道是缓冲的,所以即使作业被发送,作业也可能 工人还没有收到。
不,结果不一样:
在许多情况下,您的主 goroutine 在 received job
goroutine 之前退出(例如,不同的 CPU 负载,并且它是不确定的和系统相关的行为),因此您无法保证 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
参见:
Why is time.sleep required to run certain goroutines?
我认为已接受的答案没有详细说明原因。
go语言属于过程范式,即每条指令都是线性执行的。当一个 go 例程从主 go 例程派生出来时,它会开始自己的小冒险,将主线程留给 return.
buffered channel 的容量为5,这意味着在缓冲区满之前不会阻塞。如果它是空的,它也会阻塞(容量为零的通道本质上是无缓冲的)。
由于只有4次迭代(0到<=3),读取操作不会阻塞。
通过指示主线程从 done 通道读取,我们强制主线程等待,直到 done 通道中有一些数据要使用。迭代结束后执行else分支,写操作done <- true
导致主线程中的<- done
读操作释放。读取操作等待从 done
.
从 done
读取后,Go 主程序不再阻塞,因此成功终止。