zoukankan      html  css  js  c++  java
  • Go语言学习之Go协程:信道

    前言

    goroutine是go语言程序的并发执行的基本单元,多个goroutine的通信是需要依赖channel,叫做信道

    1.信道的定义与使用

    每个信道只能传递一种数据类型的数据,所以声明信道的时候,需要指定数据类型(string、int等)

    信道实例 := make(chan 信道类型)

    假如我要创建一个可以传递int数据类型的信道

    //定义信道
    pipline := make(chan int)

    信道的数据操作,无非就是两种:发送数据和读取数据

    // 往信道中发送数据
    pipline<- 200
    
    // 从信道中取出数据,并赋值给mydata
    mydata := <-pipline

    信道用完了,可以对其进行关闭,避免有人一直在等待

    close(pipline)

    对于一个已经关闭的信道 如果再去关闭是会报错的,所以我们需要判断一下 信道 是不是被关闭

    当从信道读取数据时,可以有多个返回值,其中第二个可以表示信道是否被关闭,如果已经被关闭就返回false,如果还没被关闭返回true

    x, ok := <-pipline

    2.信道的容量和长度

    一般创建信道都是使用make函数,make函数接收两个参数

    第一个参数: 必填  必定信道类型

    第二个参数: 选填  不填默认为0,指定信道的容量(可以缓存多少数据)

    对于信道的容量很重要:

    • 当容量为0时,说明信道中不能存放数据,在发送数据时,必须要求立马有人接收,否则会报错。此时的信道称之为无缓冲信道
    • 当容量为1时,说明信道中只能缓存一个数据,如果信道中已经有一个数据,此时再往里发送数据,会造成阻塞;可以利用信道这点做锁
    • 当容量大于1时,信道中可以存放多个数据,可以用于多个协程间的通信管道,共享资源

    我们现在知道 信道就是一个容器

    如果将它比作一个纸箱子

    • 它可以装10本书,代表其容量为10
    • 当前只装了1本书,代表其当前长度为1

    信道的容量可以用cap()函数获取,信道的长度可以用len()函数来获取

    package main
    
    import "fmt"
    
    func main() {
        pipline := make(chan int, 10)
        fmt.Printf("信道可缓冲 %d 个数据
    ", cap(pipline))
        pipline<- 1
        fmt.Printf("信道中当前有 %d 个数据", len(pipline))
    }

    输出如下

    信道可缓冲 10 个数据
    信道中当前有 1 个数据

    3.缓冲信道无缓冲信道

    按照是否可缓冲数据可分为:缓冲信道无缓冲信道

    缓冲信道

    允许信道里存储一个或多个数据,这意味着,设置了缓冲区后,发送端和接收端可以处于异步的状态

    pipline := make(chan int, 10)

    无缓冲信道

    在信道里无法存储数据,这意味着,接收端必须先于发送端准备好,以确保你发送完数据和,有人立马接收数据,否则发送端就会造成阻塞

    原因很简单,信道中无法存储数据;也就是说发送端和接收端是同步运行的

    pipline := make(chan int)
    
    // 或者
    pipline := make(chan int, 0)

    4.双向通道和单向通道

    通常情况下,我们定义的都是双向信道,可以发送数据,也可以接收数据

    但有时候,我们希望对信道的数据流做一些控制,比如这个信道只能接收数据或者这个信道只能发送数据

    因此就有了双向信道单向信道两种分类

    4.1双向信道

    默认情况下你定义的信道都是双向的,比如下面代码

    import (
        "fmt"
        "time"
    )
    
    func main() {
        pipline := make(chan int)
    
        go func() {
            fmt.Println("准备发送数据: 100")
            pipline <- 100
        }()
    
        go func() {
            num := <-pipline
            fmt.Printf("接收到的数据是: %d", num)
        }()
        // 主函数sleep,使得上面两个goroutine有机会执行
        time.Sleep(1)
    }

    4.2单向信道

    可以细分为只读信道只写信道

    定义只度信道

    var pipline = make(chan int)
    type Receiver = <-chan int // 关键代码:定义别名类型
    var receiver Receiver = pipline

    定义只写信道

    var pipline = make(chan int)
    type Sender = chan<- int  // 关键代码:定义别名类型
    var sender Sender = pipline

    仔细观察区别在于<-符号在关键字chan的左边还是右边

    • <-chan 表示这个信道,只能从里发出数据,对于程序来说就是只读

    • chan<- 表示这个信道,只能从外面接收数据,对于程序来说就是只写

    为什么要先定义一个双向信道,在定义一个单向信道呢,比如这样写

    import (
        "fmt"
        "time"
    )
     //定义只写信道类型
    type Sender = chan<- int  
    
    //定义只读信道类型
    type Receiver = <-chan int 
    
    func main() {
        var pipline = make(chan int)
    
        go func() {
            var sender Sender = pipline
            fmt.Println("准备发送数据: 100")
            sender <- 100
        }()
    
        go func() {
            var receiver Receiver = pipline
            num := <-receiver
            fmt.Printf("接收到的数据是: %d", num)
        }()
        // 主函数sleep,使得上面两个goroutine有机会执行
        time.Sleep(1)
    }

    信道本身就是为了传输数据而存在的,如果只有接收者或者只有发送者,那信道就变成了只入不出或者只出不入,那就没意义了.

     

    5.遍历信道

    遍历信道,可以搭配使用for搭配range关键字,在range时,要确保信道处于关闭状态,否则循环会阻塞

    import "fmt"
    
    func fibonacci(mychan chan int) {
        n := cap(mychan)
        x, y := 1, 1
        for i := 0; i < n; i++ {
            mychan <- x
            x, y = y, x+y
        }
        // 记得 close 信道
        // 不然主函数中遍历完并不会结束,而是会阻塞。
        close(mychan)
    }
    
    func main() {
        pipline := make(chan int, 10)
    
        go fibonacci(pipline)
    
        for k := range pipline {
            fmt.Println(k)
        }
    }

    当容量为1时,说明信道只能缓存一个数据,若信道中已有一个数据,此时再往里发送数据,会造成程序阻塞。 利用这点可以利用信道来做锁

  • 相关阅读:
    C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files/编译错误
    连接数据库配置 System.Data.Common.DbProviderFactory
    c# socket 发送邮件
    code first 数据迁移
    adb shell monkey
    汇编语言学习小总结
    zookeeper快速安装配置
    编译自己的Hadoop Eclipse插件
    在Windos上使用Eclipse进行Hadoop开发的环境搭建
    Hive安装配置以及整合HBase和hadoop
  • 原文地址:https://www.cnblogs.com/chadiandianwenrou/p/13886441.html
Copyright © 2011-2022 走看看