Go 中的 Channels —— range 与 select
本文是Go 中的 Channels的下半部分,如果你还没有阅读过第一部分,建议你先阅读它。
这里我们还有一个问题,那就是数据的接收者无法知道什么时候应该停止等待数据,是否还有更多新数据或者所有数据都已经接收到了?我们应该继续等待还是可以进行下一步的工作了?一种解决办法是不断的循环查询数据来源是否已经关闭,如果Channel已经关闭,则知道数据已经全部接收完成了,但是这并不十分有效。Go提供了一个关键字 *range*,它可以帮助我们一直监听Channel直到它关闭。
package main
import (
"fmt"
"time"
"strconv"
)
func makeCakeAndSend(cs chan string, count int) {
for i := 1; i <= count; i++ {
cakeName := "Strawberry Cake " + strconv.Itoa(i)
cs <- cakeName // 传递一个 cake
}
}
func receiveCakeAndPack(cs chan string) {
for s := range cs {
fmt.Println("Packing received cake: ", s)
}
}
func main() {
cs := make(chan string)
go makeCakeAndSend(cs, 5)
go receiveCakeAndPack(cs)
du,_ := time.ParseDuration("3s")
time.Sleep(du)
}输出为:
Packing received cake: Strawberry Cake 1
Packing received cake: Strawberry Cake 2
Packing received cake: Strawberry Cake 3
Packing received cake: Strawberry Cake 4
Packing received cake: Strawberry Cake 5对于 makeCakeAndSend*来说,我们已经指定了要制作的蛋糕数,但是对于 *receiveCakeAndPack*来说,它事先并不知道将有多少个蛋糕需要它打包,在上一个示例中,我们将这个蛋糕数硬编码至代码中,所以,它知道什么时候完成,但是现在我们改用了 *range 这个关键字,现在当 channel 关闭之后,循环会自动退出。
Channel 与 select
我们还有一种情况,那就是多台生产蛋糕的电脑可以通过多个传送带向同一个打包的电脑传送蛋糕,因为理论上讲蛋糕生产速度是没有打包速度快的,所以,我现在两人台电脑同时生产蛋糕,然后配备一台电脑用来打包,这个时候我们可以使用一个传送带(一个Channel):
package main
import (
"fmt"
"time"
"strconv"
)
func makeCakeAndSend(cs chan string, flavor string, count int) {
for i := 1; i <= count; i++ {
cakeName := flavor + " Cake " + strconv.Itoa(i)
cs <- cakeName // 传递一个 cake
}
}
func receiveCakeAndPack(cs chan string) {
for s := range cs {
fmt.Println("Packing received cake: ", s)
}
}
func main() {
cs := make(chan string)
go makeCakeAndSend(cs, "Strawberry", 5)
go makeCakeAndSend(cs, "Chocolate", 5)
go receiveCakeAndPack(cs)
du,_ := time.ParseDuration("2s")
time.Sleep(du)
}运行结果为:
Packing received cake: Strawberry Cake 1
Packing received cake: Chocolate Cake 1
Packing received cake: Strawberry Cake 2
Packing received cake: Chocolate Cake 2
Packing received cake: Strawberry Cake 3
Packing received cake: Chocolate Cake 3
Packing received cake: Strawberry Cake 4
Packing received cake: Chocolate Cake 4
Packing received cake: Strawberry Cake 5
Packing received cake: Chocolate Cake 5但是我们还可以使用一个 select 关键字来以另一种方式选择,我们为了提供生产效率,除了采用两人台电脑生产蛋糕之外,我们还使用两个传送带来传送,这样就不会因为传送带一次只能传递一个蛋糕而影响效率了:
package main
import (
"fmt"
"time"
"strconv"
)
func makeCakeAndSend(cs chan string, flavor string, count int) {
for i := 1; i <= count; i++ {
cakeName := flavor + " Cake " + strconv.Itoa(i)
cs <- cakeName
}
}
func receiveCakeAndPack(strbry_cs chan string, choco_cs chan string) {
strbry_closed, choco_closed := false, false
for {
if (strbry_closed && choco_closed) { return }
fmt.Println("Waiting for a new cake ...")
select {
case cakeName, strbry_ok := <- strbry_cs:
if (!strbry_ok) {
strbry_closed = true
fmt.Println("... Strawberry channel closed!")
} else {
fmt.Println("Received from Strawberry channel. Now packing ", cakeName)
}
case cakeName, choco_ok := <- choco_cs:
if (!choco_ok) {
choco_closed = true
fmt.Println("... Chocolate channel closed!")
} else {
fmt.Println("Received from Chocolate channel. Now packing ", cakeName)
}
}
}
}
func main() {
scs := make(chan string)
ccs := make(chan string)
go makeCakeAndSend(scs, "Strawberry", 3)
go makeCakeAndSend(ccs, "Chocolate", 3)
go receiveCakeAndPack(scs,ccs)
du, _ := time.ParseDuration("3s")
time.Sleep(du)
}输出结果为:
Waiting for a new cake ...
Received from Chocolate channel. Now packing Chocolate Cake 1
Waiting for a new cake ...
Received from Chocolate channel. Now packing Chocolate Cake 2
Waiting for a new cake ...
Received from Strawberry channel. Now packing Strawberry Cake 1
Waiting for a new cake ...
Received from Chocolate channel. Now packing Chocolate Cake 3
Waiting for a new cake ...
Received from Strawberry channel. Now packing Strawberry Cake 2
Waiting for a new cake ...
Received from Strawberry channel. Now packing Strawberry Cake 3
Waiting for a new cake ...
评论已关闭