• golang学习笔记---channel&goroutine


    什么是channel

    从字面上看,channel的意思大概就是管道的意思。channel是一种go协程用以接收或发送消息的安全的消息队列,channel就像两个go协程之间的导管,来实现各种资源的同步。可以用下图示意:

    channel的用法很简单:

    func main() {
        ch := make(chan int, 1) // 创建一个类型为int,缓冲区大小为1的channel
        ch <- 2 // 将2发送到ch
        n, ok := <- ch // n接收从ch发出的值
        if ok {
            fmt.Println(n) // 2
        }
        close(ch) // 关闭channel
    }

    使用channel时有几个注意点:

    • 向一个nil channel发送消息,会一直阻塞;
    • 向一个已经关闭的channel发送消息,会引发运行时恐慌(panic)
    • channel关闭后不可以继续向channel发送消息,但可以继续从channel接收消息;
    • channel关闭并且缓冲区为空时,继续从从channel接收消息会得到一个对应类型的零值。

    Unbuffered channels与Buffered channels

    Unbuffered channels是指缓冲区大小为0的channel,这种channel的接收者会阻塞直至接收到消息,发送者会阻塞直至接收者接收到消息,这种机制可以用于两个goroutine进行状态同步;Buffered channels拥有缓冲区,发送者在将消息发送到缓冲区之前是阻塞的,当缓冲区已满时,发送者会阻塞;当缓冲区为空时,接收者会阻塞。

    引用The Nature Of Channels In Go中的两张图来说明Unbuffered channelsBuffered channels, 非常形象,读者可自行体会一下:

    Unbuffered channels

    Unbuffered channelsUnbuffered channels

    Buffered channels

    Buffered channelsBuffered channels

    channel的遍历

    for range

    channel支持 for range 的方式进行遍历:

    package main  
    
    import "fmt"  
    
    func main() {  
        ci := make(chan int, 5)  
        for i := 1; i <= 5; i++ {
            ci <- i
        }    
        close(ci)  
    
        for i := range ci {  
            fmt.Println(i)  
        }  
    } 

    值得注意的是,在遍历时,如果channel 没有关闭,那么会一直等待下去,出现 deadlock 的错误;如果在遍历时channel已经关闭,那么在遍历完数据后自动退出遍历。也就是说,for range 的遍历方式时阻塞型的遍历方式。

    for select

    select可以处理非阻塞式消息发送、接收及多路选择。

    package main  
    
    import "fmt"  
    
    func main() {  
        ci := make(chan int, 2)
        for i := 1; i <= 2; i++ {
            ci <- i
        }
        close(ci)
    
        cs := make(chan string, 2)
        cs <- "hi"
        cs <- "golang"
        close(cs)
    
        ciClosed, csClosed := false, false
        for {
            if ciClosed && csClosed {
                return
            }
            select {
            case i, ok := <-ci:
                if ok {
                    fmt.Println(i)
                } else {
                    ciClosed = true
                    fmt.Println("ci closed")
                }
            case s, ok := <-cs:
                if ok {
                    fmt.Println(s)
                } else {
                    csClosed = true
                    fmt.Println("cs closed")
                }
            default:
                fmt.Println("waiting...")
            }
        }
    }  
    

    select中有case代码块,用于channel发送或接收消息,任意一个case代码块准备好时,执行其对应内容;多个case代码块准备好时,随机选择一个case代码块并执行;所有case代码块都没有准备好,则等待;还可以有一个default代码块,所有case代码块都没有准备好时执行default代码块。

  • 相关阅读:
    Tomcat7,Tomcat8 的manager 配置
    JavaScript 获得当前日期+时间
    CentOS 安装jdk 1.8
    nginx文件结构与解析,例子
    jxl解析多个excel工作表-java代码
    一个上传图片,预览图片的小demo
    利用DES,C#加密,Java解密代码
    CentOS安装Nginx
    摄像机标定--很懒的方法
    slam-g2o安装失败的解决问题
  • 原文地址:https://www.cnblogs.com/saryli/p/13354957.html
Copyright © 2020-2023  润新知