senderOnly := make(chan<- int) // 只能用来发送(管道的入口,只进不出)
receiverOnly := make(<-chan int) // 只能用来接收(管道的出口,只出不进)
unbuffer := make(chan int) // 无缓冲可收发
buffer := make(chan int, 2) // 无缓冲可收发
println(senderOnly, receiverOnly, unbuffer, buffer)
打印的是四个内存地址, 发现方法println, 和fmt.Println
区别在于一个输出到stderr,另一个是stdout,一个有返回值,另外一个没有。一个用fmt包支持,另外一个可能将来就没有了。
使用场景1 等待goroutine完成
理解了 go func 假如执行很长时间, main中只要其他代码逻辑执行完, goroutine不管有没有执行完都会推出。管道可以等他执行完了以后再退出。
源代码
package main
import "fmt"
func main() {
// 声明
chan1 := make(chan bool)
fmt.Println("func main start")
go func() {
fmt.Println("do something")
// 发送true值
chan1 <- true
}()
// 接收
<- chan1
close(chan1)
fmt.Println("end func main")
}
以上是源代码,为了好玩,我们改下
package main
import (
"fmt"
"time"
)
func main() {
// 声明
chan1 := make(chan bool)
fmt.Println("func main start")
go func() {
for i := 0; i < 10; i++ {
fmt.Println("do something")
time.Sleep(time.Second)
}
// 发送true值
chan1 <- true
}()
// 接收
<- chan1
close(chan1)
fmt.Println("end func main")
}
为了说明问题,我们把管道去掉。
package main
import (
"fmt"
"time"
)
func main() {
// 声明
//chan1 := make(chan bool)
fmt.Println("func main start")
go func() {
for i := 0; i < 10; i++ {
fmt.Println("do something")
time.Sleep(time.Second)
}
// 发送true值
//chan1 <- true
}()
// 接收
//<- chan1
//close(chan1)
fmt.Println("end func main")
}
可以看到代码输出
func main start
end func main
go 里面根本没运行。
2, 多个go协同
和程序里面提到的管道一样, 一个go的输出是另一个的输入。 利用管道在多个go中传值。
ok, 不是这个例子,例子中三块 go逻辑在操纵一个变量x, x被放入管道后, 三个go func 会对其进行累计处理。让我来写一个类似代码。
package main
func main() {
chanString := make(chan string)
// 这是个结巴, 大概说了三次可
s := "我"
go func() {
println("come into goroutine1")
for i := 0; i<3; i++ {
s += "可"
}
chanString <- s
}()
// 说了两个能
go func() {
println("come into goroutine2")
for j := 0; j < 2; j++ {
s += "能"
}
chanString <- s
}()
//说了四个要
go func() {
println("come into goroutine3")
for j := 0; j < 2; j++ {
s += "要"
}
chanString <- s
}()
// 注意这段代码
var result string
for i := 0; i < 3; i++ {
result += <-chanString
}
close(chanString)
println(result)
}
比较难理解的是 最后一个for循环。
尝试理解下, 第一,二个程序其实都利用了管道的 (放出原则), 就是接收时,
<- chan
他没接收到就会挂起,等待接收,所以程序不会立刻退出,都是接收完了才退出。
那个for循环,为啥必须写3, 写4,或2都会报错呢?写2是几率型报错。
是因为你有三个 go func。 其实里面的for是无意义的,
这样看简单点
package main
func main() {
chanString := make(chan string)
// 这是个结巴, 大概说了三次可
s := "我"
go func() {
println("come into goroutine1")
s += "可"
chanString <- s
}()
// 说了两个能
go func() {
println("come into goroutine2")
s += "能"
chanString <- s
}()
//说了四个要
go func() {
println("come into goroutine3")
s += "要"
chanString <- s
}()
// 注意这段代码
var result string
for i := 0; i < 1; i++ {
result += <-chanString
}
close(chanString)
println(result)
}
就是如果for i《1 时,报错,
panic: send on closed channel
我往管道发送值,但已经没人接了, 他只接收了一条。
当for 4次的时候,
fatal error: all goroutines are asleep - deadlock!
所有 goroutines 已完毕, 你还在挂起等待接收,所以报错。
3 select
两个goroutine无直接关联,但其中一个先达到某一设定条件便退出或超时退出
package main
import "time"
func main() {
println("start main")
cond1 := make(chan int)
cond2 := make(chan uint64)
go func() {
for i := 0; ; i++ {
cond1 <- i
}
}()
go func() {
var i uint64
for ; ; i++ {
cond2 <- i
}
}()
endCond := false
for endCond != true {
select {
case a := <-cond1:
if a > 99 {
println("end with cond1")
endCond = true
}
case b := <-cond2:
if b == 100 {
println("end with cond2")
endCond = true
}
case <-time.After(time.Microsecond):
println("end with timeout")
endCond = true
}
}
println("end main")
}
很有意思的代码,利用
for endCond != true
一旦
endCond = true
输出
println("end main"), 整个程序退出。
有疑问加站长微信联系(非本文作者)