Go笔记(十四):通道 channel
1、通道
通道channel是Go提供的一种用于各个协程(goroutine)之间的数据共享,保证数据同步交换的机制。协程是轻量级线程,类似于Java中的线程。
2、通道的类型
2.1、无缓冲通道
用于同步通信,可保证在发送和接收数据时完成两个goroutine(协程)的数据交换。
2.2、缓冲通道
用于异步通信,可接收到一个或多个值之前保存它们。通道中没有接收的值时,接收会阻塞;通道中没有可用的缓存区存放正在发送的值时,发送会阻塞。
3、通道的声明
声明通道是需指定数据类型,数据在通道上传递,任何给定时间只有一个goroutine可访问数据项,因此不会发生数据竞争。
通道由make函数创建,需指定chan关键字和通道的元素类型。
·创建无缓冲通道
// 字符串无缓冲通道 make(chan string)
·创建有缓冲通道
// 字符串有缓冲通道 make(chan string, 10)
使用内置函数make创建无缓冲和缓冲通道,make的第一个参数需要关键字chan,然后是通道允许交换的数据类型。
代码示例如下:
1 package main 2 3 import ( 4 "fmt" 5 "math/rand" 6 "time" 7 ) 8 9 // 创建string类型通道,只能传入int类型值 10 var vc = make(chan int) 11 12 func send() { 13 // 生成随机数 14 rand.Seed(time.Now().UnixNano()) 15 value := rand.Intn(10) 16 fmt.Printf("send: %v\n", value) 17 // 睡眠1s 18 time.Sleep(time.Second) 19 // 将数据写入通道 20 vc <- value 21 } 22 23 func main() { 24 // 关闭通道 25 defer close(vc) 26 // 创建一个协程执行send 27 go send() 28 fmt.Println("wait...") 29 // 将通道的值写入value变量中 30 value := <-vc 31 fmt.Println("value : ", value) 32 fmt.Println("send...") 33 }
执行结果如下:
4、通道接收及发送数据
1、通道接收数据
ch := make(chan string, 3) // 字符串缓冲通道 ch <- "hello" // 将数据写入通道
2、通道发送数据
data := <-通道变量名
示例代码如下:
1 package main 2 3 import ( 4 "fmt" 5 "time" 6 ) 7 8 // 创建通道 9 var ch01 = make(chan string, 5) 10 11 func main() { 12 13 // 关闭通道 14 defer close(ch01) 15 16 go func() { 17 str := "hello" 18 fmt.Printf("send: %v\n", str) 19 time.Sleep(time.Second) 20 ch01 <- str 21 }() 22 23 fmt.Println("wait...") 24 // 将通道的值写入value变量中 25 value := <-ch01 26 fmt.Println("value : ", value) 27 fmt.Println("over...") 28 29 }
执行结果如下:
5、通道的遍历
用for range遍历通道获取数据,若通道关闭,读多写少,没有了就是默认值;若没有关闭,会造成死锁。
1 package main 2 3 import "fmt" 4 5 func main() { 6 // 通道未关闭,读多写少,没有了就是默认值 7 ch01 := make(chan int) 8 go func() { 9 // 关闭通道 10 defer close(ch01) 11 // 写数据进入通道 12 for i := 0; i < 2; i++ { 13 ch01 <- i 14 } 15 }() 16 for i := 0; i < 5; i++ { 17 data := <-ch01 18 fmt.Printf("ch01 data: %v\n", data) 19 } 20 fmt.Println("----------------------") 21 // for rang遍历 22 ch02 := make(chan int) 23 go func() { 24 // 关闭通道 25 defer close(ch02) 26 // 写数据进入通道 27 for i := 0; i < 10; i++ { 28 ch02 <- i 29 } 30 }() 31 for v := range ch02 { 32 fmt.Printf("ch02 date: %v\n", v) 33 } 34 }
执行结果如下:
6、通道的发送和接收特性
1、对于同一个通道,发送操作之间是互斥的,接收操作也是互斥的;
2、发送和接收操作是原子的;
3、发送操作在完全完成之前会被阻塞。接收操作也是如此。