Go并发编程 2| 青训营笔记

119 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天

channel

介绍

Channel是 Golang 在语言级别上提供的 goroutine 间的通讯方式,可以使用channel在多个 goroutine 之间传递消息。如果说 goroutine 是 Go 程序并发的执行体,channel 就是它们之间的连接。channel 是可以让一个 goroutine 发送特定值到另一个 goroutine 的通信机制。 Golang 的并发模型是 CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。 Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明 channel 的时候需要为其指定元素类型。

声明

声明的方法如下

var 变量名称 chan 元素类型

声明后的通道不能直接使用,我们需要make来分配内存

格式为:通道实例 := make(chan 元素类型, [缓冲大小])

如果不设置缓冲大小,那么将声明一个无缓冲区通道,该通道只有sender和receiver都准备好时,才能进行通信。

channel操作

发送(send)

通过 <- 操作符来向通道内发送数据 例如:

func main() {  
    //创建通道ch  
    ch := make(chan int,2)  
    //从通道里面发送数据  
    ch <- 10 //将10发送到通道ch中  
}

接收(receive)

通过 <- 操作符从通道中接收数据

例如:x := <- ch 代表用x变量接受了通道ch内的一个数据

关闭(close)

格式为:close(ch)

读取通道内的数据

通过for range 来读取通道内所有的数据(当通道还未关闭时)

单向channel

只读通道(只能从通道中读取)

声明格式为:var ch <- chan int

只写通道(只能从通道中读取)

声明格式为:var ch chan <- int

select关键字

select只能用于io操作,一般配合channel使用。 例子:

select {
	case msg1 := <-c1:
		fmt.Println("c1 received:",msg1)
	case msg2 := <-c2:
		fmt.Println("c2 received",msg2)
	default:
		fmt.Println("No data received")
}
    

意思为哪个管道先有数据就先执行哪个分支,select不保证顺序执行,是随机执行的

通过channel实现广播

可利用已关闭的channel读取数据时总是非阻塞的特性,可以实现在一个协程中向其他多个协程广播某个事件发生的通知,例如定义一个exitp chan any来控制所有go程退出

package main
import (
 "fmt"
 "time"
)

type aa struct {
 message chan any
 name    string
}

func (p *aa) Run() {
 defer close(p.message)
 for {
  select {
  case ss := <-p.message:
   fmt.Println(p.name, ss)
  case <-Exitp:
   fmt.Println("exit :", p.name)
   return
  }
 }
}

var Eventbus chan any
var Exitp chan any
var Reicevermap map[string]*aa

func main() {
 Exitp = make(chan any) //控制go程退出
 Eventbus = make(chan any, 1)
 Reicevermap = make(map[string]*aa)
 a := &aa{make(chan any, 3), "aa"} //aa接收者
 go a.Run()                        //aa接收广播线程
 Reicevermap[a.name] = a
 b := &aa{make(chan any, 3), "bb"} //bb接收者
 go b.Run()                        //bb接收广播线程
 Reicevermap[b.name] = b
 fmt.Println("111")
 go broadcast()
 time.Sleep(time.Duration(1) * time.Second)
 Eventbus <- "第一个广播"
 time.Sleep(time.Duration(3) * time.Second)
 close(Exitp)
 time.Sleep(time.Duration(3) * time.Second)
}

func broadcast() {
 for event := range Eventbus {//不建议此种方式接收chan数据,建议用select
  for _, v := range Reicevermap {
   v.message <- event
  }
 }
}