-
Channel
- 是阻塞同步的
- 通过make创建,close关闭
- Channel是引用类型
- 可以使用for range 来迭代不断操作channel
- 可以设置单向或双向通道
- 可以设置缓存大小,在未被填满前不会发生阻塞
-
Select
- 可以处理一个或多个channel的发送与接收
- 同时有多个可用的channel时按随机顺序处理
- 可用空的select来阻塞main函数
- 可设置超时
一个简答的例子
...
func main(){
go work()
}
func work(){
fmt.Println("Go!")
}
输入:
(什么都没有)
什么都没有输出,原因是由代码可以看出,启动了一个携程,在work函数还没有打印主函数已经结束了。
为了让work里的打印可以输出,让主线程睡1秒中,这是为了让携程可以执行完毕,代码修改如下:
...
func main(){
go work()
time.sleep(time.Second)
}
func work(){
fmt.Println("Go!")
}
以上仅仅是为了效果,在实际的开发中不会这样做,因为我们无法判断work函数的执行逻辑到底需要多长时间。
借助Channel,替换time.sleep
package main
import (
"fmt"
)
func main() {
c:= make(chan bool)
go work(c)
<- c //这里会阻塞,直到c里有值
}
func work(c chan bool){
fmt.Println("Go Go Go!")
c <- true
}
输出:
Go Go Go !
注意:以上是个简单的程序,只向channel放了一个值,当取出后,资源就会自动释放了,程序就结束了,所以不用再调用close关闭通道了。
上面的程序从通道里取值,没有赋值给任何变量也没有输出,如下使用range从通道里将值打印出来:
package main
import (
"fmt"
)
func main() {
c:= make(chan bool)
go work(c)
<- c
for v:= range c{
fmt.Println(v)
}
}
func work(c chan bool){
fmt.Println("Go Go Go!")
c <- true
close(c)
}
输出:
Go Go Go!
true
以上程序修改了:11-13行使用range 将值打印出来, 19行调用了close函数关闭通道,如果不关闭通道,就会不断的遍历通道造成死锁。
for i := range c
能够不断的读取channel里面的数据,直到该channel被显式的关闭。关闭channel之后就无法再发送任何数据了,在消费方可以通过语法v,ok := <-ch
测试channel是否被关闭。如果ok返回false,那么说明channel已经没有任何数据并且已经被关闭。
设置有缓存的通道
c:= make(chan bool,1)
有缓存的就是channel可以存储多少元素。ch:= make(chan bool, 4)
,创建了可以存储4个元素的bool 型channel。在这个channel 中,前4个元素可以无阻塞的写入。当写入第5个元素时,代码将会阻塞,直到其他goroutine从channel 中读取一些元素,腾出空间。
有缓存和无缓存的区别:有缓存的是异步的,无缓存是同步阻塞的。如下示例:
package main
import (
"fmt"
)
func main() {
c:= make(chan bool,1)
go func(){
fmt.Println("Go Go Go!")
<- c
}()
c <- true
}
以上程序不会有任何输出,以上创建了有缓存的通道,在执行14行的时候,程序不会阻塞,就不会等待携程的执行。
package main
import (
"fmt"
)
func main() {
c:= make(chan bool,1)
go func(){
fmt.Println("Go Go Go!")
c <- true
}()
<- c
}
输出:
Go Go Go!
缓存通道的例子
package main
import (
"fmt"
"runtime"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
c := make(chan bool,10)
for i := 0; i < 10; i++ {
go Go(c, i)
}
for i := 0; i < 10; i++ {
<-c
}
}
func Go(c chan bool, index int) {
a := 1
for i := 0; i <= 10000000; i++ {
a += i
}
fmt.Println(index, a)
c <- true
}
实现以上程序的另一个办法:
package main
import (
"fmt"
"sync"
)
func main() {
wg:= sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 10; i++ {
go Go(&wg, i)
}
wg.Wait()
}
func Go(wg *sync.WaitGroup, index int) {
a := 1
for i := 0; i <= 10000000; i++ {
a += i
}
fmt.Println(index, a)
wg.Done()
}
输出:
0 50000005000001
5 50000005000001
9 50000005000001
1 50000005000001
7 50000005000001
6 50000005000001
3 50000005000001
2 50000005000001
4 50000005000001
8 50000005000001