第十九:channel 的使用
单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义。
Go语言采用的并发模型是CSP(Communicating Sequential Processes)
,提倡通过通信共享内存而不是通过共享内存而实现通信。
channel
是可以让一个 goroutine 发送特定值到另一个 goroutine 的通信机制。
Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素类型。
channel类型,定义一个channel 的格式
注意:channel 通道是一个引用类型,通道类型的空值是 nil
channel
是 Go 语言中一种特有的类型。声明通道类型变量的格式如下:
var 变量名称 chan 元素类型
其中:
- chan:是关键字
- 元素类型:是指通道中传递元素的类型
举几个例子:
var ch1 chan int // 声明一个传递整型的通道
var ch2 chan bool // 声明一个传递布尔型的通道
var ch3 chan []int // 声明一个传递int切片的通道
channel零值
未初始化的通道类型变量其默认零值是nil
。
var ch chan int
fmt.Println(ch) // <nil>
初始化channel
声明的通道类型变量需要使用内置的make
函数初始化之后才能使用。具体格式如下:
make(chan 元素类型, [缓冲大小])
其中:
- channel的缓冲大小是可选的。
举几个例子:
ch4 := make(chan int)
ch5 := make(chan bool, 1) // 声明一个缓冲区大小为1的通道
channel操作
通道共有发送(send)、接收(receive)和关闭(close)三种操作。而发送和接收操作都使用<-
符号。
现在我们先使用以下语句定义一个通道:
ch := make(chan int)
发送
将一个值发送到通道中。
ch <- 10 // 把10发送到ch中
接收
从一个通道中接收值。
x := <- ch // 从ch中接收值并赋值给变量x
<-ch // 从ch中接收值,忽略结果
关闭
我们通过调用内置的close
函数来关闭通道。
close(ch)
关闭后的通道有以下特点:
- 对一个关闭的通道再发送值就会导致 panic。
- 对一个关闭的通道进行接收会一直获取值直到通道为空。
- 对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
- 关闭一个已经关闭的通道会导致 panic。
通道可以通过<-操作符进行发送和接收数据,例如:
ch <- data // 发送数据到通道
result := <-ch // 从通道接收数据
单向 通道数据
发后 chan<- // 往里面发数据
<-chan 取前 // 在这个里面取数据
无缓冲的channel
要求发送和接收必须同步,即发送和接收操作必须同时准备好,才能完成数据传递。
package main
import (
"fmt"
)
func sendData(ch chan int) {
ch <- 10 // 将数据发送到 channel
fmt.Println("Data sent to channel")
}
func main() {
ch := make(chan int) // 创建无缓冲 channel
go sendData(ch) // 启动一个 goroutine 发送数据
data := <-ch // 从 channel 接收数据
fmt.Println("Received data:", data)
}
Go语言协程之间如何进行数据共享和同步?
在Go语言中,可以使用互斥锁(mutex)来实现协程之间的数据共享和同步。互斥锁是一种机制,用于保护共享资源的访问,防止多个协程同时修改数据。
要使用互斥锁,首先需要导入"sync"包,然后创建一个互斥锁对象:
import "sync"
var mutex sync.Mutex
在需要修改共享资源的地方,可以使用互斥锁进行保护,例如:
mutex.Lock() // 加锁
// 修改共享资源的代码
mutex.Unlock() // 解锁
通过互斥锁的加锁和解锁操作,可以确保同一时间只有一个协程能够修改共享资源,从而实现数据的安全共享和同步。
总结:Go语言协程可以通过通道进行通信,通过互斥锁进行数据共享和同步。这些机制使得并发编程变得简单且高效,适用于各种并发场景。