🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## channel **不要通过共享内存来通信,而要通过通信来实现内存共享。** 这就是Go语言的并发哲学,他依赖于CSP,基于channel实现。 ## channel的操作 channel的操作分为创建、发送、接收、关闭等4个操作,下面就从源码和流程方面分开展示 ## 创建chan ``` //无缓冲的通道 ch1 := make(chan int) //有缓冲的通道 ch2 := make(chan int , 1) ``` ## 一、不同协程之间如何通讯 * 全局变量加锁同步 * channel 1、改进使用全局变量加锁同步改进程序 * 因为没有对全局变量加锁,因此会出现资源夺取问题,代码会出现错误,提示concurrent map writes * 加入互斥锁 2、全局变量加锁同步缺陷 * 主线程在等待所有goroutine全部完成的时间很难确定 * 如果主线程休眠时间长了,会加长等待时间,如果等待时间短了,可能还有goroutine处于工作状态,这时也会随着主线程的结束而结束 * 不利于多个协程对全局变量的读写操作 基于以上的缺陷我们可以使用管道来解决 ## 二、管道基本介绍 * 管道本质介绍一个数据结构-队列 * 数据是先进先出 * 线程安全,无需加锁 * 管道有类型 ## 三、管道基本使用 ## 四、管道关闭和遍历 #### 1、关闭 使用内置函数close可以关闭channel,关闭后,就不能写入数据,但可读 #### 2、遍历 * 在使用for--range遍历时,如果channel没有关闭,则回出现deadlock错误 * 在使用for--range遍历时,如果channel已经关闭,则会正常遍历数据 ``` package main import "fmt" func main() { //定义管道 var intChan chan int intChan =make(chan int,3) //写入数据 intChan<-10 intChan<-20 intChan<-30 //遍历 close(intChan) //关闭管道 for value := range intChan { fmt.Printf("%d\t",value) //10 20 30 } } ``` ## 案例 #### 案例1 1)启动一个协程,将1-2000的数放到一个channel中,比如numChan 2)启动8个协程,从numChan中取出数(比如n),并计算1+...+的值,并存放到resChan 3)最后8个协程协同完成工作后,再遍历resChan,显示结果[如res[1]=1 .. res[10]=55 ..] ``` package main import "fmt" func main() { numChan := make(chan int, 2000) resChan := make(chan int, 2000) exitChan := make(chan bool, 8) go putNum(numChan) //存放数据 //开启八个协程 for i := 0; i < 8; i++ { go add(numChan, resChan, exitChan) } go func() { for i:=0;i<8 ;i++ { <-exitChan } close(resChan) }() for i := 1; i <=2000 ; i++ { fmt.Printf("resChan[%d]=%d\n", i, <-resChan) } } func putNum(numChan chan int) { for i := 1; i <= 2000; i++ { numChan <- i } close(numChan) } func add(numChan chan int, resChan chan int, exitChan chan bool) { for { n,ok := <-numChan if !ok{ break } res := 0 for i := 1; i <= n; i++ { res += i } resChan <- res } exitChan<-true } ``` 推荐:[https://juejin.cn/post/7142386254641168397](https://juejin.cn/post/7142386254641168397)