比如:放进一个 []string,也不能单次取出一个 string
type 通道名 chan 通道中数据类型
type ch1 chan int
1)初始化方法
make(chan 通道中数据类型[, N])
N表示同一时刻最多可以容纳N个元素值
make (chan int,10)
2)缓冲/非缓冲通道
一个通道类型的值的缓冲容量是固定不变的,发送给非缓冲通道的的元素值必须被立刻取走。
通道名 <- 变量名|值
c <- "aaa"
input函数将数组names传入通道
func input( c chan string) {
names := []string{"关羽", "张飞", "赵云", "马超","黄忠"}
for _,x := range names {
c <- x
}
close(c)
}
func main() {
c := make(chan string, 10)
input(c)
for i := range c {
fmt.Printf("hello %s \n",i)
}
time.Sleep(3*time.Second)
}
通道写满
如果通道写满,向通道中写入的协程将被阻塞,直到通道中数据被接收时该协程被唤醒。
传入通道的值仅是副本
变量将值传给通道之后,变量值变化与通道中数据无关。
func main() {
name := "关羽"
c := make(chan string,3)
defer close(c)
c <- name
name = "张飞"
if i,ok := <- c;ok {
fmt.Println("channel :",i)
fmt.Println("name :",name)
}
}
输出
channel : 关羽
name : 张飞
可见 name 改为张飞之后,channel里的值依然是关羽。
变量名[,布尔值] := <-通道名
示例
name := <- c
或
name,ok := <- c
ok值:如果读出数据为true ,未读出为false(比如通道中没有数据了)
ok值的作用:作为一个布尔值,可用作之后的if判断,如下:
name,ok := <- c
if ok{
go fmt.Println(name)
}else {
break
}
func input( c chan string) {
names := []string{"关羽", "张飞", "赵云", "马超","黄忠"}
for _,x := range names {
c <- x
}
close(c)
}
func main() {
c := make(chan string, 10)
input(c)
for {
name,ok := <- c
if ok{
go fmt.Println(name)
}else {
break
}
}
time.Sleep(3*time.Second)
}
打印结果
没有顺序,五个协程谁先打印出结果看命。
黄忠
关羽
张飞
赵云
马超
此时通道会被永远阻塞
close(通道名)
示例
close(c1)
关闭时机
建议在输入端关闭
通道关闭之后仍可以读出
示例
一个通道
两个函数 input()写,output()读,
两个协程分别执行读写函数
关闭通道在写函数中
func input( c chan string) {
names := []string{"关羽", "张飞", "赵云", "马超","黄忠"}
for _,x := range names {
c <- x
}
close(c)
}
func output (c chan string ){
for i := range c {
fmt.Println("hello ",i)
}
}
func main() {
c := make(chan string, 1)
go input(c)
go output(c)
time.Sleep(3*time.Second)
}
1)概念
2)演示
我们将上例修改一下,初始化的时候,设定通道容量为8,打印长度和容量,然后将读取调用注释掉。
func input( c chan string) {
names := []string{"关羽", "张飞", "赵云", "马超","黄忠"}
for _,x := range names {
c <- x
}
close(c)
}
func output (c chan string ){
for i := range c {
fmt.Println("hello ",i)
}
}
func main() {
c := make(chan string,8)
input(c)
//output(c)
fmt.Println("通道长度是: ",len(c))
fmt.Println("通道容量是: ",cap(c))
time.Sleep(3*time.Second)
}
输出
通道长度是: 5
通道容量是: 8
输入的5个元素都停留在通道中,所以长度是5。
而通道容量是我们初始化时候的容量。
func main() {
c := make(chan string,8)
input(c)
fmt.Println("通道长度是: ",len(c))
fmt.Println("通道容量是: ",cap(c))
output(c)
fmt.Println("读取后通道长度是: ",len(c))
fmt.Println("读取后通道容量是: ",cap(c))
time.Sleep(3*time.Second)
}
输出
通道长度是: 5
通道容量是: 8
hello 关羽
hello 张飞
hello 赵云
hello 马超
hello 黄忠
读取后通道长度是: 0
读取后通道容量是: 8
可见,读取后通道长度变为0,而通道容量不变。(有兴趣的话你可以打印每次写入/读取时的通道长度)
发送通道:
通道名 chan<- 传入数据类型
传出通道:
通道名 <-chan 传出数据类型
var in chan<- string
var out <-chan string
说明:
- 定义输入函数,形参为发送通道
- 定义传出函数,形参为接收通道
- 主函数中定义一个双向通道
- 主函数调用传入函数,该通道为实参(该函数中为发送通道)。
- 主函数调用传出函数,该通道位实参(该函数中为接收通道)。
func main() {
chan1 := make(chan string,10)
go input(chan1) //调用发送通道函数
go output(chan1) //调用接收通道函数
time.Sleep(1*time.Second)
}
func input(in chan<- string) {
names := []string{"关羽", "张飞", "赵云", "马超","黄忠"}
for _,i := range names {
in <- i
}
close(in)
}
func output(out <-chan string) {
for i := range out {
fmt.Println("hello ", i)
}
}
hello 关羽
hello 张飞
hello 赵云
hello 马超
hello 黄忠
func main() {
chan1 := make(chan string,10)
go input(chan1) //调用发送通道函数
go output(chan1) //调用接收通道函数
time.Sleep(1*time.Second)
}
说明:
如果我们要求传入传出两个通道分开(chan1、chan2),那么我们还需要用一个函数将chan1的数据写入chan2。
- 定义输入函数,形参为发送通道
- 定义传出函数,形参为接收通道
- 定义转换函数,形参为发送函数和接收函数
- 主函数中定义两个双向通道chan1、chan2
- 主函数调用传入函数,以chan1位实参(chan1为发送通道)。
- 主函数调用传出函数,以chan2位实参。(chan2为接收通道)
- 主函数调用转换函数,实参和调用传出、传入函数相反(即:chan2为发送通道,chan1为接收通道),从而将chan1的数据写入chan2.
func main() {
chan1 := make(chan string)
chan2 := make(chan string)
go input(chan1) //调用发送通道函数
go in2out(chan2, chan1) //将发送通道的数据写入接收通道
go output(chan2) //调用接收通道
time.Sleep(1*time.Second)函数
}
func input(in chan<- string) {
names := []string{"关羽", "张飞", "赵云", "马超","黄忠"}
for _,i := range names {
in <- i
}
close(in)
}
func output(out <-chan string) {
for i := range out {
fmt.Println("hello ", i)
}
}
func in2out(out chan<- string, in <-chan string) {
for i:= range in {
out <- i
}
close(out)
}
前边示例一直在用,注意一下通道未初始化的问题,其他不多说了。
另外,下边这个示例读写我没用协程,注意通道容量要给够,否则报错。(当然使用协程不存在这个问题)
package main
import (
"errors"
"fmt"
"time"
)
func input( c chan string) error{
names := []string{"关羽", "张飞", "赵云", "马超","黄忠"}
for _,x := range names {
if c == nil {
return errors.New("通道为nil")
}
c <- x
}
close(c)
return nil
}
func output (c chan string ) error{
if c == nil {
return errors.New("通道为nil")
}
for i := range c {
fmt.Println("hello ",i)
}
return nil
}
func main() {
c := make(chan string, 8)
//var c chan string //注释掉初始化,打开本行看报错
err := input(c)
if err != nil {
fmt.Println(err)
}
err = output(c)
if err != nil {
fmt.Println(err)
}
time.Sleep(3*time.Second)
}
作用:通道的选择
示例
要求:
1)两个协程分别向两个通道中写入蜀将和魏将的名字
2)将结果分别打印出来。
思路:
创建两个函数,分别向两个通道c1、c2中写。
主函数启动两个协程,分别调用两个写函数。
主函数启动一个协程(避免for的死循环)分别打印两个通道中的数据
代码:
func inputShu( c chan string) {
names := []string{"关羽", "张飞", "赵云", "马超","黄忠"}
for _,x := range names {
c <- x
}
close(c)
}
func inputWei( c chan string) {
names := []string{"张辽", "乐进", "于禁", "张合","徐晃"}
for _,x := range names {
c <- x
}
close(c)
}
func main() {
c1 := make(chan string,8)
c2 := make(chan string,8)
go inputShu(c1)
go inputWei(c2)
go func() {
for {
time.Sleep(500*time.Millisecond)
select {
case msg1,ok := <-c1:
//if msg1 == ""{continue}
if ok {
fmt.Println("蜀将: ", msg1)
}
case msg2,ok := <-c2:
if ok {
fmt.Println("魏将: ", msg2)
}
default:
fmt.Println("hello world")
}
}
}()
time.Sleep(10*time.Second)
}
输出:
蜀将: 关羽
魏将: 张辽
蜀将: 张飞
魏将: 乐进
蜀将: 赵云
蜀将: 马超
魏将: 于禁
蜀将: 黄忠
魏将: 张合
魏将: 徐晃
概念:
容量设置成0,或者直接忽略对容量的设置的通道
特点:
只能同步的传递发送给它的元素值
非缓冲通道的“happens before”原则:
发送操作会等待接收操作完成后结束
func main() {
unbufChan := make(chan string)
go func() {
fmt.Println("( • - •) 孔明在睡觉")
time.Sleep(5*time.Second)
fmt.Println("很久以后~~~~~~~孔明睡醒了")
massage := <-unbufChan
fmt.Printf("( • - •) 孔明收到了 《%s》\n", massage)
}()
massage:= "先生请出山"
fmt.Printf("[ x - x] 主公来了 %s\n", massage)
unbufChan <- massage
fmt.Println("[ x - x] 主公走了")
}
输出:
[ x - x] 主公来了 先生请出山
( • - •) 孔明在睡觉
很久以后~~~~~~~孔明睡醒了
( • - •) 孔明收到了 《先生请出山》
[ x - x] 主公走了
执行可见,主函数的输入等待到协程读取通道之后才结束。
和缓冲通道中的使用几乎相同
值得关注的是:
- 非缓冲通道必须使用协程
- 缓冲通道在容量够大的情况下,一个协程就可以完成读写。
func main() {
chan1 := make(chan string)
input(chan1) //调用发送通道函数
output(chan1) //调用接收通道函数
time.Sleep(1*time.Second)
}
func input(in chan<- string) {
names := []string{"关羽", "张飞", "赵云", "马超","黄忠"}
for _,i := range names {
in <- i
}
close(in)
}
func output(out <-chan string) {
for i := range out {
fmt.Println("hello ", i)
}
}
若同是 for循环+协程的应用,通道是否有缓冲,在使用上并没有太大区别
func inputShu( c chan string) {
names := []string{"关羽", "张飞", "赵云", "马超","黄忠"}
for _,x := range names {
c <- x
}
close(c)
}
func inputWei( c chan string) {
names := []string{"张辽", "乐进", "于禁", "张合","徐晃"}
for _,x := range names {
c <- x
}
close(c)
}
func main() {
c1 := make(chan string)
c2 := make(chan string)
go inputShu(c1)
go inputWei(c2)
func() {
for {
time.Sleep(500*time.Millisecond)
select {
case msg1,ok := <-c1:
//if msg1 == ""{continue}
if ok {
fmt.Println("蜀将: ", msg1)
}
case msg2,ok := <-c2:
if ok {
fmt.Println("魏将: ", msg2)
}
default:
fmt.Println("hello world")
}
}
}()
time.Sleep(10*time.Second)
}