GO语言实战十 管道

Celeste ·
更新时间:2024-11-13
· 523 次阅读

上一篇记录了goroutine操作共享数据时保证对共享资源的安全访 问以及消除竞争状态

原子操作 互斥锁
这里介绍通道 不要通过共享内存来通信,而应该通过通信来共享内存 什么是通道

Channel 也是Go语言里的一种引用类型,通道可以被认为是Goroutines通信的管道。类似于管道中的水从一端到另一端的流动,数据可以从一端发送到另一端,通过通道接收。
当一个资源需要在 goroutine 之间共享时,通道在 goroutine 之间架起了一个管道,并提供了 确保同步交换数据的机制。声明通道时,需要指定将要被共享的数据的类型。可以通过通道共享 内置类型、命名类型、结构类型和引用类型的值或者指针。
无缓冲的通道保证同时交换数据,而有缓冲的通道不做这种保证

创建通道

使用 make函数 和关键词 chan创建通道,使用 <- 运算符 进行赋值和读取操作

//创建 无缓冲通道 整型 unbuffer := make(chan int) //创建 有缓冲通道 字符串型 buffer := make(chan string, 10) fmt.Printf("%T=====%T",unbuffer,buffer) //chan int=====chan string通道收到了 test1 buffer <- "test1" //从通道接收字符串 value := <-buffer fmt.Println("通道收到了",value)//通道收到了 test1 无缓冲通道

无缓冲的通道(unbuffered channel)是指在接收前没有能力保存任何值的通道。这种类型的通 道要求发送 goroutine 和接收 goroutine 同时准备好,才能完成发送和接收操作。如果两个 goroutine 没有同时准备好,通道会导致先执行发送或接收操作的 goroutine 阻塞等待。这种对通道进行发送 和接收的交互行为本身就是同步的。其中任意一个操作都无法离开另一个操作单独存在。
在这里插入图片描述

两个 goroutine 都到达通道,但哪个都没有开始执行发送或者接收。 左侧 的 goroutine 将它的手伸进了通道,这模拟了向通道发送数据的行为。这时,这个 goroutine 会在 通道中被锁住,直到交换完成。 右侧的 goroutine 将它的手放入通道,这模拟了从通 道里接收数据。这个 goroutine 一样也会在通道中被锁住,直到交换完成。 在第 4 步和第 5 步, 进行交换,并最终, 在第 6 步,两个 goroutine 都将它们的手从通道里拿出来,这模拟了被锁住 的 goroutine 得到释放。两个 goroutine 现在都可以去做别的事情了。 func unbufferTest(){ court := make(chan int) wg.Add(2) go play("张三",court) go play("李四", court) //发球 court<-1 wg.Wait() } //模拟选手打球 func play(name string, court chan int) { defer wg.Done() for { //等待球打过来 ball,ok := <-court if !ok{ //如果通道关闭则我们赢了 fmt.Println("winner is ",name) return } n := rand.Intn(100) if n%5 == 0{ fmt.Println("missied ",name) close(court) return } fmt.Printf("选手 %s 打出了第%d次球\n",name,ball) ball++ //打球给对方 court<-ball } } 输出 选手 李四 打出了第1次球 选手 张三 打出了第2次球 missied 李四 winner is 张三 有缓冲通道

有缓冲的通道(buffered channel)是一种在被接收前能存储一个或者多个值的通道。这种类 型的通道并不强制要求 goroutine 之间必须同时完成发送和接收。通道会阻塞发送和接收动作的 条件也会不同。只有在通道中没有要接收的值时,接收动作才会阻塞。只有在通道没有可用缓冲 区容纳被发送的值时,发送动作才会阻塞。这导致有缓冲的通道和无缓冲的通道之间的一个很大 的不同:无缓冲的通道保证进行发送和接收的 goroutine 会在同一时间进行数据交换;有缓冲的 通道没有这种保证。
在这里插入图片描述

package main import ( "fmt" "math/rand" "sync" "time" ) /** * 有缓冲通道 */ var wg sync.WaitGroup const ( //开启的goroutine数量 numberGoroutine = 4 //要处理的工作的数量 taskLoad = 10 ) func init() { // 初始化随机数种子 rand.Seed(time.Now().UnixNano()) } func main() { //createChan() bufferTest() } func bufferTest() { tasks := make(chan string, taskLoad) wg.Add(numberGoroutine) for line := 1; line <= numberGoroutine; line++ { go work(tasks, line) } addWork(tasks) close(tasks) wg.Wait() } //增加工作 func addWork(tasks chan string) { for i := 1; i <= taskLoad; i++ { tasks <- fmt.Sprintf("工作任务%d", i) } } //模拟选手打球 func work(tasks chan string, worker int) { defer wg.Done() for { //等待球打过来 task, ok := \n", worker, task) } } 输出: 工人4开始干活了,干的是工作任务1 工人2开始干活了,干的是工作任务2 工人3开始干活了,干的是工作任务3 工人1开始干活了,干的是工作任务4 工人4 完成了工作任务《工作任务1》> 工人4开始干活了,干的是工作任务5 工人4 完成了工作任务《工作任务5》> 工人4开始干活了,干的是工作任务6 工人1 完成了工作任务《工作任务4》> 工人1开始干活了,干的是工作任务7 工人2 完成了工作任务《工作任务2》> 工人2开始干活了,干的是工作任务8 工人1 完成了工作任务《工作任务7》> 工人1开始干活了,干的是工作任务9 工人4 完成了工作任务《工作任务6》> 工人4开始干活了,干的是工作任务10 工人2 完成了工作任务《工作任务8》> 工作结束了 工人1 完成了工作任务《工作任务9》> 工作结束了 工人3 完成了工作任务《工作任务3》> 工作结束了 工人4 完成了工作任务《工作任务10》> 工作结束了 第 46 行中关闭通道的代码非常重要。当通道关闭后,goroutine 依旧可以从通道接收数据, 但是不能再向通道 里发送数据
作者:coder~



go语言实战 go语言 实战 GO 管道

需要 登录 后方可回复, 如果你还没有账号请 注册新账号