ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

go语言学习笔记 — 进阶 — 并发编程(6):通道(channel)—— 使用channel收发数据

2021-10-25 11:32:11  阅读:221  来源: 互联网

标签:接收数据 进阶 goroutine go 接收 data channel 通道


创建channel之后,我们可以使用channel进行数据收发操作。

使用channel发送(填充)数据

使用特殊的操作符<-,把数据通过channel发送。

  • 格式
通道变量 <- 值

通道变量是上文通过make创建的通道实例;值可以是变量、常量、表达式或函数返回值,值类型必须与make(chan 数据类型)中的数据类型一致。

  • 发送数据

在使用make创建channel后,就可以使用<-向通道发送数据。

ch := make(chan interface{}) // 创建一个空接口通道

ch <- 0        // 把整型数据放入通道
ch <- "hello"  // 把字符串放入通道
  • 若数据未被接收,则发送数据将持续阻塞,直到数据被接收

把数据向通道发送,如果接收方一致没有接收,那么发送操作将持续阻塞,直到数据被接收方接收。

package main

func main() {
    ch := make(chan int)
    ch <- 0
}

// fatal error: all goroutines are asleep - deadlock!

go会智能发现一些永远无法发送成功的语句,并提示。上面的意思是,go运行时发现所有goroutine(包括main()函数的)都处于waiting状态,即所有goroutine并没有发送或接收数据对应的通道。


使用channel接收(取出)数据

通道接收(取出)数据的特性

  • 通道收发数据需要在两个不同的goroutine之间进行

通过channel发送数据,如果通道中没有另外一个goroutine接收,数据发送方goroutine会持续阻塞,直到接收方goroutine接收数据;通过channel接收数据,如果通道中没有发送方goroutine发送数据,数据接收方goroutine将持续阻塞,直到发送方goroutine发送数据为止。

  • 通道一次只能接收一个元素数据

通道接收(取出)数据的格式

一共有4种通道接收数据的写法。

  • 阻塞模式接收数据

把接收变量作为<-操作符的左值。执行此语句,发送方将会持续阻塞,直至接收方接收数据并赋值给data变量。

data := <-ch
  • 非阻塞模式接收数据(高CPU占用,少用)

使用非阻塞方式从通道接收数据,语句不会阻塞。

data, ok := <-ch

data表示接收数据的通道变量,在没有接收到数据时,data为通道类型的零值。ok表示是否成功从通道接收到数据。

  • 接收任意数据,忽略接收的数据

阻塞模式接收数据后,忽略从通道返回的数据。

<-ch

执行此语句,将会发生数据接收阻塞,直至接收到数据,但接收到的数据会被忽略。此方式使用channel在不同goroutine间阻塞收发,实现并发同步。

package main

import "fmt"

func main() {
    ch := make(chan int)  // 构建一个同步通道
    
    go func() {           // 开一个匿名函数的并发
        fmt.Println("start goroutine")
        ch <- 0           // 匿名函数快结束时,通过channel告知main()函数的goroutine,数据发送会一直阻塞,直到main()函数的goroutine接收为止。 
        fmt.Println("exit goroutine")
    }()

    fmt.Println("wait goroutine")
    
    <-ch  // 开启匿名函数的goroutine后,阻塞模式等待发送数据,接收任意数据后忽略
    
    fmt.Println("all done")
}

/*
wait goroutine
start goroutine
exit goroutine
all done
*/
  • 循环接收

通道数据可以使用for range语句,进行多个元素的接收。遍历得到变量data。格式如下:

for data := range ch {
    循环体
}

通道是可遍历的,遍历结果是接收到的数据,其数据类型是通道的元素类型。

package main

func main() {
    ch := make(chan int)  // 生成一个整型元素的通道
    
    go func() {           // 把匿名函数并发执行
        for i := 3;i >= 0; i-- {
            ch <- i       // 使用通道发送值i
            time.Sleep(time.Second) // 每次发送完暂停1秒
        }
    }()
   
   for data := range ch {  // 使用for range从通道中接收数据
       fmt.Println(data)   // 打印接收的变量
       
       if data == 0 {      // 当接收到0时,终止for range
           break           // 如果发送端继续发,接收goroutine已经推出,会报panic
       }
   }
}

/*
3
2
1
0
*/

使用goroutine和channel,实现并发打印

设计生产者与消费者模型:生产者是main函数的for循环,消费者是printer函数。整个模型使用两个goroutine:一个是main(),另一个是go printer创建的goroutine,两个goroutine通过第21行的通道进行通信。

这个通道有两个功能:

  • 数据传送:第26行发送数据,第9行接收数据
  • 控制指令:类似信号量的功能,同步goroutine操作。如第17行、29行、31行
package main

import "fmt"

// 定义打印函数
func printer(c chan int) {
    
    for {              // 启动一个死循环
        data := <-c    // 从函数入参channel中取得整型数据
        if data == 0 { 
            break
        }
    
        fmt.Println(data) // 输出刚取得的整型数据   
    }
    
    c <- 0  // 退出循环时,通知main函数(我搞定了)
}

func main() {
    c := make(chan int)  // 创建一个channel
    
    go printer(c)  // 并发执行,传入channel
    
    for i := 1; i <= 10; i++ { // 使用for循环,把1~10通过通道c发送给printer函数的goroutine
        c <- i
    }
    
    c <- 0  // 发完数据后,通知并发的printer结束循环(没数据了)
    
    <-c     // 阻塞式等待接收printer发送退出数据,然后main函数才能结束(搞定喊我),
} 

标签:接收数据,进阶,goroutine,go,接收,data,channel,通道
来源: https://blog.csdn.net/yjk13703623757/article/details/120942714

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有