文章目录
Go并发编程(二)Channel
channel通信机制
在go中推荐使用基于消息的并发通信,而不是通过共享内存,通过channel机制,实现goroutine相互通信
channel是进程内的通讯方式,是不支持跨进程通信的,如果需要进程间通讯的话,可以使用Socket等网络方式。
使用
通过channel机制,实现goroutine相互通信,管道是类型相关的,即一个管道只能传递一种类型的值。管道中的数据是先进先出的。
func workGO(c chan int) {
fmt.Println("---协程工作---")
time.Sleep(10 * time.Second)
num := <- c
fmt.Println("接收到的值:",num)
}
func MyChannel(){
c := make(chan int,10) // 声明缓冲区大小,接收到的值先发送到缓冲区,不阻塞协程
defer close(c) // 关闭通道
go workGO(c)
fmt.Println("主线程")
// 传递2给管道,默认是阻塞式管道,即一定要读取出来后才能继续执行
c <- 2
fmt.Println("协程从管道中读取出数值")
time.Sleep(10 * time.Second)
}
// 单向管道
var ch1 chan<- float64 // 只能向里面写入float64的数据,不能读取
var ch2 <-chan int // 只能读取int型数据
// 关闭channel,直接调用close()即可
close(ch)
// 判断ch是否关闭,判断ok的值,如果是false,则说明已经关闭(关闭的话读取是不会阻塞的)
x, ok := <-ch
阻塞式:
先阻塞住
后接收到后解除阻塞
带缓冲区:
channel + select实现非阻塞管道通信,接收不到数据则走select default分支,避免阻塞
func send(c chan int) {
for i :=1 ; i<10 ;i++ {
c <-i
fmt.Println("send data : ",i)
}
}
func TestSelect() {
a := make(chan int)
b := make(chan int)
go send(a)
// time.Sleep(3 * time.Second)
select{
case one := <-a:fmt.Println("a:",one)
case two := <-b:fmt.Println("b:",two)
default:
fmt.Println("default")
}
}
实现原理
在Java中实现线程间通信依靠的是共享内存的方式,采用同步锁来保证共享内存通信的线程安全问题,但是在Go中既可以通过共享内存实现goroutine间通信,也可以基于管道消息实现goroutine通信,并且在Go中更鼓励通过后者的方式。
Go中提出了通信顺序模型CSP模型(Communicating sequential processes),goroutine和channel分别是CSP的通信实体和媒介
上图中的两个 Goroutine,一个会向 Channel 中发送数据,另一个会从 Channel 中接收数据,它们两者能够独立运行并不存在直接关联,但是能通过 Channel 间接完成通信。
Go中的channel在运行时底层是runtime.hchan结构体:
type hchan struct {
// channel中元素容量
qcount uint // total data in the queue
// 循环队列长度
dataqsiz uint // size of the circular queue
// 队列缓冲区数据指针
buf unsafe.Pointer // points to an array of dataqsiz elements
// 元素数量
elemsize uint16
// 管道是否已关闭
closed uint32
// 元素类型
elemtype *_type // element type
// 发送指针
sendx uint // send index
// 接收指针
recvx uint // receive index
// sendq 和 recvq 存储了当前 Channel 由于缓冲区空间不足而阻塞的 Goroutine 列表
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
//
// Do not change another G's status while holding this lock
// (in particular, do not ready a G), as this can deadlock
// with stack shrinking.
lock mutex
}