• GO语言-管道channel


    概述

            通道channel可以被认为是goroutine通信的管道。关于前一篇go语言锁的博客中也提到,解决临界资源问题可以使用锁,但是更建议的是使用channel来实现goroutine之间的通信。

    不要通过共享内存来通信,而应该通过通信来共享内存。

    • 需要注意是,channel本身就是同步的。意味着同一时间只有一条goroutine来操作channel。
    • 死锁——如果一个channel只有读数据,而没有写数据,则会产生死锁。反之亦然。如果写代码时不小心造成了死锁,go语言会抛出相关异常。

    channel通道的创建和使用

    channel声明

    每个通道都有其相关的类型,该类型就是通道内允许传输的数据类型。

    空的通道为nil,nil的通道没有任何用处。所以通道的声明是和map类似的。

    1. //声明通道
    2. var channel_name chan type
    3. //初始化通道
    4. channel_name = make(chan type)

    简短声明

    channel_name := make(chan type)

    channel读写操作

    1. data := <- channel_name //将通道中的值赋值给变量
    2. channel_name <- data //将变量的值写入通道
    3. value, ok := <- channel_name //从一个channel中读取值

    channel的读写操作都是阻塞的

    正常情况下,如果不使用sync.WaitGroup同步等待组、锁,主函数中的子goroutine很可能在还没有执行时,因为主函数的结束而一起结束了。

    但是可以通过“channel的读写操作都是阻塞的这一特性”,子goroutine通过channel通道传递信息给主goroutine,主goroutine在读到数据前会一直阻塞。

    1. func main() {
    2. channel1 := make(chan bool)
    3. go func(){
    4. for i:=0;i<10;i++{
    5. fmt.Println(i)
    6. }
    7. channel1 <- true
    8. fmt.Println("子goroutine结束...")
    9. }()
    10. data1 := <- channel1
    11. fmt.Println("channel传递的值:",data1)
    12. fmt.Println("主函数结束...")
    13. }

    关闭channel通道

    发送者可以通过关闭channel通道,告知接收方不会有更多的数据被发送到channel上了。

    close(channel_name)

    接受者可以接受来自channel读到数据时,使用额外的变量来检查通道是否关闭。

    ok为true时,表示从通道中读取了一个value;当ok是false是,意味着正从一个封闭的通道读取数据,读取到的value都将是零值。

    value, ok := <- channel_name

    1. var channel1 = make(chan int)
    2. func main() {
    3. go fun1()
    4. for{
    5. fmt.Println("从通道中读取值...")
    6. v,ok := <-channel1
    7. time.Sleep(1*time.Second)
    8. if !ok {
    9. fmt.Println("channel读取结束,通道关闭",v,ok)
    10. break
    11. }
    12. fmt.Println("通道中的值:",v,ok)
    13. }
    14. }
    15. func fun1() {
    16. for i:=0;i<10;i++{
    17. time.Sleep(1*time.Second)
    18. fmt.Println("通道中写入值:", i)
    19. channel1 <- i
    20. }
    21. close(channel1)
    22. }

    channel通道的范围循环

    通过for_range增强循环,不需要自己判断通道是否关闭。range会判断通道是否关闭。

    1. var channel1 = make(chan int)
    2. func main() {
    3. go fun1()
    4. for v := range channel1 { // v <- channel1
    5. time.Sleep(1 * time.Second)
    6. fmt.Println(v)
    7. }
    8. }
    9. func fun1() {
    10. for i := 0; i < 10; i++ {
    11. channel1 <- i
    12. }
    13. close(channel1)
    14. }

    缓冲通道

    带缓冲区的通道,会把数据先写入缓冲区中。发送数据时,只有当缓冲区中满了才会被阻塞;接收数据时,只有缓冲区为空的时候才会被阻塞。

    ch := make(chan type, Size)

    Size需要大于0,使通道具有缓冲区。默认情况无缓冲区通道的容量为0,所以省略了该参数。

    1. var channel1 = make(chan int,5) //创建一个容量5的缓冲通道
    2. func main() {
    3. go fun1()
    4. for v := range channel1 {
    5. time.Sleep(1 * time.Second)
    6. fmt.Println("\t读出数据",v)
    7. }
    8. }
    9. func fun1() {
    10. for i := 0; i < 10; i++ {
    11. channel1 <- i
    12. fmt.Println("写入数据:",i)
    13. }
    14. close(channel1)
    15. }

    定向通道

    双向通道

    前面我们创建的通道,都是双向通道。双向通道是可以,一个goroutine发送数据,一个goroutine接收数据的。

    定向通道(单向通道)

    单向通道也就是定向通道。单向通道只能发送或者接收数据。

    定向通道创建语法

    1. channel1 := make(chan <-int) //只能写入数据,不能读数据
    2. channel2 := make(<- chan int) //只能读数据,不能写入数据

    定向通道的用法

    往往创建和使用通道的时候通常还是创建双向通道。单向通道只用来作为函数参数类型

    只是在将通道作为参数传递到函数时,函数中通道的类型使用单向通道。限制通道在函数内部使用时是只能读不能写的;或者是只能写不能读的。

  • 相关阅读:
    MySQL基础|在Navicat中输入SQL语句步骤【含SQL语句约束规范】
    C语言第十课(上):编写井字棋游戏(综合练习1)
    【RabbitMQ 实战】12 镜像队列
    C# WinForm —— 07 Form窗体介绍
    JavaScript:实现AvlTree树算法(附完整源码)
    Java8 Stream流式操作接口详解
    HTML+CSS大作业【传统文化艺术耍牙15页】学生个人网页设计作品
    SPDK线程模型
    Matlab 如何选择采样频率和信号长度
    C语言实例练习
  • 原文地址:https://blog.csdn.net/qq522044637/article/details/125445676