跳到主要内容

Go 通道

提示
  1. 通道作为通信媒介: 在Go中,通道允许goroutines之间进行通信,用于处理并发程序中的资源共享和通信。
  2. 创建和操作通道: 使用make()函数创建通道,并通过 <- 运算符向通道发送数据或从通道接收数据。
  3. 通道的阻塞特性: Go中的通道具有阻塞特性,发送操作会阻塞直到另一个goroutine接收数据,反之亦然。

Go 中的通道作为协程之间进行通信的媒介。

我们知道协程用于创建并发程序。并发程序可以同时运行多个进程。

然而,有时可能会出现两个或更多协程需要相互通信的情况。在这种情况下,我们使用通道,允许协程之间通信和共享资源。

在学习通道之前,请确保了解 Go 中的协程是如何工作的

在 Go 中创建通道

在 Go 中,我们使用 make() 函数来创建通道。例如,

channelName := make(chan int)

这里,

  • channelName - 通道的名称
  • (chan int) - 表示通道是整数类型的

示例:Go 通道

package main
import "fmt"

func main() {

// 创建整数类型的通道
number := make(chan int)

// 访问通道的类型和值
fmt.Printf("通道类型: %T\n", number)
fmt.Printf("通道值: %v", number)

}

输出

通道类型: chan int
通道值: 0xc00007a060

在上面的例子中,我们使用了 make() 函数创建了名为 number 的通道。这里,我们使用了格式说明符:

  • %T - 打印通道的类型
  • %v - 打印通道的值

由于通道是整数类型的(由 chan int 指定),我们得到了相同的输出。

另外,通道的值是一个内存地址,作为协程发送和接收数据以进行通信的媒介。

Golang 通道操作

创建通道后,我们可以通过通道在不同协程之间发送和接收数据。

1. 向通道发送数据

发送数据到通道的语法是

channelName <- data

这里 <- 运算符后的数据被发送到 channelName。

让我们看一些例子,

// 向通道发送整数数据
number <- 15

// 发送字符串数据
message <- "学习 Go 通道"

2. 从通道接收数据

从通道接收数据的语法是:

<- channelName

这访问了 channelName 的数据。

让我们看一些例子,

// 接收数据 15
<- number

// 接收数据 "学习 Go 通道"
<- message

示例:Go 通道操作

package main
import "fmt"

func main() {

// 创建通道
number := make(chan int)
message := make(chan string)

// 通过协程调用函数
go channelData(number, message)

// 检索通道数据
fmt.Println("通道数据:", <-number)
fmt.Println("通道数据:", <-message)

}

func channelData(number chan int, message chan string) {

// 向通道发送数据
number <- 15
message <- "学习 Go 通道"

}

输出

通道数据: 15
通道数据: 学习 Go 通道

在上面的例子中,我们创建了名为 number 和 message 的两个通道。

这里,我们使用了 <- 运算符来执行从通道发送和接收数据的操作。

通道的阻塞特性

在 Go 中,根据协程的状态,通道自动阻塞发送和接收操作。

1. 当协程向通道发送数据时,操作会被阻塞,直到另一个协程接收到数据。例如,

package main
import "fmt"

func main() {

// 创建通道
ch := make(chan string)

// 通过协程调用函数
go sendData(ch)

// 接收通道数据
fmt.Println(<-ch)

}

func sendData(ch chan string) {

// 数据发送到通道
ch <- "接收成功。发送操作成功"
fmt.Println("无接收者!

发送操作阻塞")

}

输出

无接收者!发送操作阻塞
接收成功。发送操作成功

在上面的例子中,我们创建了 sendData() 协程来向通道发送数据。协程向通道发送字符串数据。

如果通道还没有准备好接收消息,它会打印 "无接收者!发送操作阻塞"

main() 函数内,我们在接收通道数据之前调用了 sendData()。这就是为什么首先打印了 "无接收者..."

当通道准备好接收数据时,协程发送的数据就会被打印。

2. 当协程从通道接收数据时,如果另一个协程还未向通道发送数据,操作会被阻塞。例如,

package main
import "fmt"

func main() {

// 创建通道
ch := make(chan string)

// 通过协程调用函数
go receiveData(ch)

fmt.Println("无数据。接收操作阻塞")

// 向通道发送数据
ch <- "数据接收。接收操作成功"

}

func receiveData(ch chan string) {

// 从通道接收数据
fmt.Println(<-ch)

}

输出

无数据。接收操作阻塞
数据接收。接收操作成功

在上面的例子中,我们创建了 receiveData() 协程来从通道接收数据。协程从通道接收字符串数据。

如果通道还未发送数据,它会打印 "无数据。接收操作阻塞"

main() 函数内,我们在向通道发送数据之前调用了 receiveData()。这就是为什么首先打印了 "无数据..."

当通道发送数据时,协程接收的数据就会被打印。