Go笔记(十四):通道 channel

发布时间 2023-05-06 08:28:59作者: 无虑的小猪

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、发送操作在完全完成之前会被阻塞。接收操作也是如此。