channel是go语言在语言级别提供的goroutine间的通信机制。我们可以使用channel在两个或者多个goroutine之间传递信息。channel是进程内的通信。
channel分为带缓冲的以及不带缓冲的。
ch:=make(chan int )创建一个不带缓冲的channel。ch:=make(chan int,2,5)创建一个带缓冲的channel,其中len(ch)是2,cap(ch)是5.
1.不带缓冲的channel使用:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go ff(ch)
fmt.Println("1", time.Now())
ch <- 2
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
}
func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
<-ch
fmt.Println("ff end")
fmt.Println("4", time.Now())
}
输出:
1 2016-05-12 12:50:02.4098584 +0800 CST3 2016-05-12 12:50:02.4118585 +0800 CST
ff end
4 2016-05-12 12:50:04.4149731 +0800 CST
2 2016-05-12 12:50:04.4149731 +0800 CST
main end
5 2016-05-12 12:50:04.4149731 +0800 CST
根据时间可以分析一下程序的工作流程。首先main函数运行到ch<-2时候就阻塞了。不带缓冲的channel接收数据后会阻塞,直到从这个channel中读取数据后,阻塞的程序才恢复。等到ff函数运行到<-ch后,main函数才继续运行。这是ff函数也在运行。从打印的时间可以看出两个函数是并发的。
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go ff(ch)
fmt.Println("1", time.Now())
<-ch
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
}
func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
ch <- 2
fmt.Println("ff end")
fmt.Println("4", time.Now())
}
输出: 1 2016-05-12 13:03:00.3583545 +0800 CST
3 2016-05-12 13:03:00.3603546 +0800 CST
ff end
4 2016-05-12 13:03:02.3634692 +0800 CST
2 2016-05-12 13:03:02.3634692 +0800 CST
main end
5 2016-05-12 13:03:02.3634692 +0800 CST
根据打印的时间信息可以分析,main函数在执行到<-ch时,阻塞。不带缓冲的channel从中读取数据时会阻塞,直到向其中写入数据,阻塞的程序才会继续执行。
2.带缓冲的channel使用:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 2)
go ff(ch)
fmt.Println("1", time.Now())
ch <- 2
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
}
func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
<-ch
fmt.Println("ff end")
fmt.Println("4", time.Now())
}
输出:1 2016-05-12 13:07:27.0666093 +0800 CST
2 2016-05-12 13:07:27.0716096 +0800 CST
main end
5 2016-05-12 13:07:27.0716096 +0800 CST
根据打印信息可以分析,main函数在执行到ch<-2的时候,没有阻塞,而是继续执行。那么为什么ff函数没有来得及运行fmt.Println("3", time.Now())这条程序呢?
从上面不带channel的程序可以看出,fmt.Println("3", time.Now())这句话运行的时间比fmt.Println("1", time.Now())这句话运行的时间要晚。晚多少时间和cpu有关。我们可以先不管。可以做出假设,ff函数里面的fmt.Println("3", time.Now())还没来得及执行,main函数就执行完了,程序就退出了。在这个过程中开的goroutine都不会执行,直接取消。
那么,怎么证明上面那段程序ff没有执行是因为main结束了呢?可以在mian函数最后等待一小会。
修改上面的程序如下:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 2)
go ff(ch)
fmt.Println("1", time.Now())
ch <- 2
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
time.Sleep(1 * time.Second)
}
func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
<-ch
fmt.Println("ff end")
fmt.Println("4", time.Now())
}
程序输出:1 2016-05-12 13:17:44.0658997 +0800 CST
2 2016-05-12 13:17:44.0708999 +0800 CST
main end
5 2016-05-12 13:17:44.0708999 +0800 CST
3 2016-05-12 13:17:44.0678998 +0800 CST
可以看到,main最后等待了一秒时间,然后ff函数来得及执行fmt.Println("3", time.Now())。但是在ff函数执行time.Sleep(2 * time.Second)等待两秒的时候,mian已经结束了。
更改一下channel读取和写入的顺序,如下:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 2)
go ff(ch)
fmt.Println("1", time.Now())
<-ch
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
}
func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
ch <- 2
fmt.Println("ff end")
fmt.Println("4", time.Now())
}
输出:1 2016-05-12 13:20:06.0520208 +0800 CST
3 2016-05-12 13:20:06.055021 +0800 CST
ff end
4 2016-05-12 13:20:08.0571355 +0800 CST
2 2016-05-12 13:20:08.0571355 +0800 CST
main end
5 2016-05-12 13:20:08.0571355 +0800 CST
根据打印信息可以分析,main函数在执行到<-ch这句话是阻塞,带缓冲的channel如果里面没有数据,从中读取的话会阻塞程序的运行。等到ff函数执行到ch <- 2时,main函数才从阻塞中恢复。
有疑问加站长微信联系(非本文作者)