GO语言实战十 管道

上一篇记录了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 阻塞等待。这种对通道进行发送 和接收的交互行为本身就是同步的。其中任意一个操作都无法离开另一个操作单独存在。
在这里插入图片描述

  1. 两个 goroutine 都到达通道,但哪个都没有开始执行发送或者接收。
  2. 左侧 的 goroutine 将它的手伸进了通道,这模拟了向通道发送数据的行为。这时,这个 goroutine 会在 通道中被锁住,直到交换完成。
  3. 右侧的 goroutine 将它的手放入通道,这模拟了从通 道里接收数据。这个 goroutine 一样也会在通道中被锁住,直到交换完成。
  4. 在第 4 步和第 5 步, 进行交换,并最终,
  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 := <-tasks
		if !ok {
			fmt.Println("工作结束了  ")
			return
		}
		//开始同坐
		fmt.Printf("工人%d开始干活了,干的是%s\n", worker, task)

		//模拟工作时间  等待时间
		sleep := rand.Int63n(100)
		time.Sleep(time.Duration(sleep) * time.Millisecond)

		//完成工作
		fmt.Printf("工人%d 完成了工作任务《%s》>\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 依旧可以从通道接收数据, 但是不能再向通道
里发送数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值