Go语言学习(十一)| 通道

mervyn 2019年3月22日14:08:37编程语言 GoGo语言学习(十一)| 通道已关闭评论192

目录

通道类型的值本身就是并发安全的,这也是 Go 语言自带的、唯一一个可以满足并发安全性的类型。文章源自编程技术分享-https://mervyn.life/ae7138ec.html

通道声明及初始化

通道相当于一个先进先出的队列。在声明一个通道类型变量的时候,我们首先要确定该通道类型的元素类型,决定了我们可以通过这个通道传递什么样的数据。文章源自编程技术分享-https://mervyn.life/ae7138ec.html

声明并初始化一个通道时需要使用内建函数 makemake 第二个参数可选,用于表示通道的容量。文章源自编程技术分享-https://mervyn.life/ae7138ec.html

ch := make(chan int, 2)

当容量为0时,该通道为 非缓冲通道 。大于0时为 缓冲通道文章源自编程技术分享-https://mervyn.life/ae7138ec.html

元素值的发送和接受都需要使用操作符 <- (也叫接送操作符)。用法如下:文章源自编程技术分享-https://mervyn.life/ae7138ec.html

  • ch <- 将元素发送至通道 ch文章源自编程技术分享-https://mervyn.life/ae7138ec.html

  • <- ch 也被叫做接收表达式,用于表达从该通道接收一个元素值。文章源自编程技术分享-https://mervyn.life/ae7138ec.html

package main

import "fmt"

func main() {
    ch := make(chan int, 3)
    ch <- 4 //像通道ch 发送一个元素值2
    ch <- 3
    ch <- 1

    elem := <- ch   //从通道中接收一个元素值
    fmt.Println(elem)   //4
    fmt.Println(len(ch))    //2
}

引发阻塞的操作

缓冲通道

如果通道已满,那么所有的发送到该通道的操作都会被阻塞,直到通道中有元素被接收走。通道会优先通知最早因此等待的那个操作所在的goroutine,再次进行发送。文章源自编程技术分享-https://mervyn.life/ae7138ec.html

如果通道已空,那么对它的所有接收操作都会被阻塞,直到通道中有新元素出现。道会优先通知最早因此等待的那个操作所在的goroutine,再次进行接收。文章源自编程技术分享-https://mervyn.life/ae7138ec.html

非缓冲通道

无论是发送还是接收,一开始执行就会被阻塞,直到出现配对的操作也开始,才会继续传递。由此可见,非缓冲通道是采用同步的方式进行传递。文章源自编程技术分享-https://mervyn.life/ae7138ec.html

值为nil的通道

对值为nil的通道进行发送和接收都会永久阻塞状态。它们所属的goroutine中的代码都不再会执行。文章源自编程技术分享-https://mervyn.life/ae7138ec.html

由于通道类型是引用类型,所以它的零值就是 nil 。所以只声明了该类型的变量但是没有用make 初始化时,它的值就会是 nil文章源自编程技术分享-https://mervyn.life/ae7138ec.html

引发panic的操作

下边罗列一下会引发 panic 的情况:文章源自编程技术分享-https://mervyn.life/ae7138ec.html

  • 通道关闭后,继续向它进行发送操作
  • 试图关闭一个已经关闭的通道

千万不要让接收方关闭通道,应该让发送方做这件事文章源自编程技术分享-https://mervyn.life/ae7138ec.html

单向通道

只能发不能收或者只能收不能发的通道就是单向通道。例:文章源自编程技术分享-https://mervyn.life/ae7138ec.html

ch := make(chan<- int, 1)

上述通道表示只能发不能收。简称为 发送通道文章源自编程技术分享-https://mervyn.life/ae7138ec.html

通道常用操作

package main

import "fmt"

func main() {
    ch := getChan()
    for elem := range ch {
        fmt.Println(elem)
    }
}

func getChan()  <-chan int {
    ch := make(chan int, 5)
    for i := 0; i < 5; i++ {
        ch <- i
    }

    close(ch)
    return ch
}

下边例子为 select 与通道联合使用文章源自编程技术分享-https://mervyn.life/ae7138ec.html

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    channels := [3]chan int {
        make(chan int, 1),
        make(chan int, 1),
        make(chan int, 1),
    }

    randInt := rand.Intn(3)
    fmt.Println("value:", randInt)
    channels[randInt] <- randInt

    select {
    case elem := <-channels[0]:
        fmt.Println("first channel, value:", elem)
    case elem := <-channels[1]:
        fmt.Println("second channel, value:", elem)
    case elem := <-channels[2]:
        fmt.Println("third channel, value:", elem)
    default:
        fmt.Println("error")

    }
}

select 有默认分支时不会被阻塞。文章源自编程技术分享-https://mervyn.life/ae7138ec.html

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int, 1)
    time.AfterFunc(time.Second, func() {
        close(ch)
    })

    select {
    case _, ok := <-ch: //判断通道是否已关闭
        if !ok {
            fmt.Println("ch is closed")
            break
        }
    }
}
文章源自编程技术分享-https://mervyn.life/ae7138ec.html
weinxin
我的微信公众号
微信扫一扫
mervyn
Go 方法指针接收者和值接收者 编程语言

Go 方法指针接收者和值接收者

Go 语言可以给自定义的类型添加一个方法。这里的方法其实也是函数,跟函数的区别在于在 func 关键字和函数名中间增加了一个参数,可以认为该类型也作为了参数传递入了函数中,例: package mai...
Go语言学习(十)| module 使用 编程语言

Go语言学习(十)| module 使用

requirement go module 是在go 1.11版本才开始有的,需要将环境变量 GO111MODULE 设置为 on 才能正常使用 例: export GO111MODULE=on 初次...
Go语言学习(四)| 数组、切片、集合 编程语言

Go语言学习(四)| 数组、切片、集合

array 声明数组 array由 <type> 定义,n为array的长度,标示希望存储的内容的类型。例: var arr int //声明一个int类型的数组 arr = 42 //数...
Go语言学习(二)| 变量 编程语言

Go语言学习(二)| 变量

Go变量的类型在变量名的后面。 声明单个变量 使用语法为: var var_name type , 例: var age int var age int = 10 //声明单个变量并初始化 声明多个变...