channel
Go语言在语言级别提供的goroutine间的通信方式,让他们之间可以进行数据交互。
声明方式:
var chanName chan ElementType
// 如:
var ch1 chan int
// 也可以使用make函数创建
ch2 := make(chan string)
上面创建了两个channel,ch1只能用来存储int类型的数据,ch2只能用来存储string类型的数据。
channel的写与读
channel的写与读也非常简单,使用<-
符号实现,<-
在channel变量名右边表示写入值,<-
在左边表示读取值。
如:
ch := make(chan int)
ch <- 10 // 向ch中写入10
res := <-ch // 读取ch中的值并赋值给res
案例:
package main
import (
"fmt"
"time"
"strconv"
)
func Read(ch chan int){
for {
value := <-ch
fmt.Println("value:" + strconv.Itoa(value))
}
}
func Write(ch chan int){
ch <- 10
}
func main(){
ch := make(chan int)
go Read(ch)
go Write(ch)
time.Sleep(10)
}
输出:
value:10
上面的代码中分别有两个函数,函数Read用来从channel中读取值,函数Write用来往channel里面写值,注意value := <-ch
在读取值时可能存在channel中无数据存在的情况,如果没有数据可读就会阻塞,直到从channel中读取到数据。因为会不断从channel中读取可能存在的数据,所以一般都会使用一个死循环来读取channel数据,避免写入channel中的数据会丢失。
channel的操作
channel都有3中操作:send、receive和close
- send:表示向channel中投放数据
- receive:表示从channel中读取数据
- close:表示关闭channel
注意:
- 关闭channel后,send操作将导致painc(抛出异常)。
- 关闭channle后,receive操作将返回对应类型的0值以及一个状态码false。
- close并非强制需要使用,在某些时候可以自动被关闭。
- 如果使用了close(). 建议条件允许的情况下加上defer
- 只在send端上显式使用close()关闭channel。因为关闭通道意味着没有数据需要发送。
案例,判断channel是否被关闭:
val, ok := <-ch
if ok{
fmt.Println(val)
}
缓冲channel
在对channel写入数据后,如果没有读取的操作,程序就会阻塞着不运行,这个时候我们想要程序不阻塞,就可以设置一个缓冲区大小,如果写入值数量超过缓冲区大小时再进行阻塞,声明channel时,可以再跟一个参数,代表缓冲区大小。
声明,如:
c := make(chan int, n) // n代表缓冲区大小
c := make(chan int) // 没写参数,代表0 等价于c := make(chan int, 0)
案例:
func test(ch chan int){
ch <- 10
fmt.Println("come to goroutine")
}
func main(){
ch := make(chan int)
go test(ch)
time.Sleep(time.Second * 3)
fmt.Println("runing end")
}
输出结果为:
runing end
因为创建的channel没有设置缓冲区大小,而且在写入值之后也没有读取值的操作,所以程序阻塞,没有打印"come to goroutine"。
修改缓冲区大小,如:
func test(ch chan int){
ch <- 10
fmt.Println("come to goroutine")
}
func main(){
ch := make(chan int,1)
go test(ch)
time.Sleep(time.Second * 3)
fmt.Println("runing end")
}
输出结果为:
come to goroutine
runing end