阅读 936

Go 语言入门与进阶:channel 实践

这是我参与更文挑战的第 28 天,活动详情查看: 更文挑战

前文回顾

如果你还没有 Go 语言基础,建议阅读我的 从零学 Go

本系列文章,我将会进一步加深对 Go 语言的讲解,更一步介绍 Go 中的包管理、反射和并发等高级特性。

了解完 Go 中 goroutine 的实践,本文将会重点介绍 channel 的使用和特点。

通道 channel

Go 中倡导使用 channel 作为 goroutine 之间同步和通信的手段。channel 类型属于引用类型,且每个 channel 只能传递固定类型的数据,channel 声明如下所示:

var channelName chan T
复制代码

我们通过 chan 关键字声明一个新的 channel,并且声明时指定 channel 内传输的数据类型 T。

channel 作为一个队列,它会保证数据收发顺序,总是遵循先入先出的原则进行;同时它也会保证同一时刻内有且仅有一个 goroutine 访问 channel 用于发送和获取数据。

从 channel 发送数据需要使用 <- 符号,如下所示:

channel <- val
复制代码

表示 val 将会发送到 channel 中,在 channel 被填满之后再往通道中将会阻塞当前 goroutine。而从 channel 读取数据也是使用符号 <-,只不过待接受的数据和 channel 的位置互换了,如下所示:

val := <- channel
复制代码

表示从 channel 中读取 val,如果 channel 中没有数据,将会阻塞读取的 goroutine 直到有数据被放入 channel。当然也可以在读取 channel 时立刻返回,如下所示:

val, ok:= <- channel
复制代码

这时需要检查 ok 是否为 true 用于判断是否读取到有效数据。

创建 channel 我们需要借助 make 函数对 channel 进行初始化,形式如下所示:

ch := make(chan T, sizeOfChan)
复制代码

在创建 channel 时需要指定 channel 传输的数据类型,可选指定 channel 的长度。如果不指定 channel 的长度,那么往 channel 中发送数据的 goroutine 将会被阻塞到数据被读取;而指定了长度的 channel 将会携带 sizeOfChan 的缓冲区,在缓冲区未满时发送数据不会被 channel 阻塞。无论 channel 是否携带缓冲区,读取的 goroutine 都会被阻塞直到 channel 中有数据可被读取。

接下来我们通过一个例子来实践 goroutine 和 channel 配合,我们将创建一个 channel 用于在两个 goroutine 中发送数据,其中一个 goroutine 从命令行读取输入发送到 channel 中,另一个 goroutine 循环从 channel 中读取数据并输出,代码如下所示:

package main

import (
	"bufio"
	"fmt"
	"os"
)

func printInput(ch chan string)  {
	// 使用 for 循环从 channel 中读取数据
	for val := range ch{
		// 读取到结束符号
		if val == "EOF"{
			break
		}
		fmt.Printf("Input is %s\n", val)
	}
}

func main()  {
	// 创建一个无缓冲的 channel
	ch := make(chan string)
	go printInput(ch)

	// 从命令行读取输入
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		val := scanner.Text()
		ch <- val
		if val == "EOF"{
			break
		}
	}
	// 程序最后关闭 ch
	defer close(ch)

}
复制代码

通过 go run 运行上述代码,在命令上中输入字符串,将会获取程序的反馈,如下所示:

Hello
Input is Hello
Hi
Input is Hi
EOF
End the game!
复制代码

channel 作为一个具备长度的容器,也是可以被遍历的。上述代码中,我们通过 for:range 语法从 channel 中循环读取数据.当 channel 中没有数据时,printInput 的 goroutine 将会被阻塞。代码的最后,我们还通过 defer close(ch) 关闭了创建的 channel,需要注意的是在 channel 关闭后不允许再往通道中放入数据,不然会抛出 panic;而从关闭的 channel 读取数据或者正在被阻塞的 goroutine 将会接受到零值,直接返回。

小结

本文主要介绍了 channel 的应用实践。Go 语言 channel 作为一个具备长度的容器,也是可以被遍历的。上述实践案例中,我们通过 for:range 语法从 channel 中循环读取数据.当 channel 中没有数据时,printInput 的 goroutine 将会被阻塞。代码的最后,我们还通过 defer close(ch) 关闭了创建的 channel,需要注意的是在 channel 关闭后不允许再往通道中放入数据,不然会抛出 panic;而从关闭的 channel 读取数据或者正在被阻塞的 goroutine 将会接受到零值,直接返回。

阅读最新文章,关注公众号:aoho求索

文章分类
后端
文章标签