Go入门: 浅谈channel

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go入门: 浅谈channel相关的知识,希望对你有一定的参考价值。

参考技术A

channel 管道在golang中是一个非常重要的概念,我之前模糊不清,对于 "输入管道" "输出管道" 所用到的 <- -> 经常分不清楚。

channel 是 goroutine 之间通信的一种方式,可以类比成 Unix 中的进程的通信方式管道。


channel 提供了一种通信机制,通过它,一个 goroutine 可以想另一 goroutine 发送消息。channel 本身还需关联了一个类型,也就是 channel 可以发送数据的类型。例如: 发送 int 类型消息的 channel 写作 chan int 。

channel在glang中实现了下面的一些功能:

1 数据交流:当作并发的 buffer 或者 queue,解决生产者 - 消费者问题。多个 goroutine 可以并发当作生产者(Producer)和消费者(Consumer)。

2 数据传递:一个 goroutine 将数据交给另一个 goroutine,相当于把数据的拥有权 (引用) 托付出去。

3 信号通知:一个 goroutine 可以将信号 (closing、closed、data ready 等) 传递给另一个或者另一组 goroutine 。

4 任务编排:可以让一组 goroutine 按照一定的顺序并发或者串行的执行,这就是编排的功能。

5 锁:利用 Channel 也可以实现互斥锁的机制。


channel 分: "只能接收" , “只能发送”, “可以接收也可以发送” 三种类型



//声明一个channel,这个channel只能保存int类型的数据。也就是说一端只能channel中写入int型的数据,另一段只能从channel中读取int型数据




关闭channel时,需注意:


从无缓存的 channel 中读取消息会 阻塞 ,直到有 goroutine 向该 channel 中发送消息;同理,向无缓存的 channel 中发送消息也会阻塞,直到有 goroutine 从 channel 中读取消息。

有缓存的 channel 的声明方式为指定 make 函数的第二个参数,该参数为 channel 缓存的容量

有缓存的 channel 类似一个阻塞队列(采用环形数组实现)。当缓存未满时,向 channel 中发送消息时不会阻塞,当缓存满时,发送操作将被阻塞,直到有其他 goroutine 从中读取消息;相应的,当 channel 中消息不为空时,读取消息不会出现阻塞,当 channel 为空时,读取操作会造成阻塞,直到有 goroutine 向 channel 中写入消息。

Go基础系列:指定goroutine的执行顺序

Go channel系列

当关闭一个channel时,会使得这个channel变得可读。通过这个特性,可以实现一个goroutine执行顺序的技巧。

如果一个goroutine A依赖于另一个goroutine B,在goroutine A中首先通过读goroutine B来阻塞自己,直到goroutine B关闭自身之后,goroutine A才会继续运行。这样,goroutine B就先于goroutine A运行。

下面是一个指定goroutine执行顺序的示例,它保证的顺序是A()-->B()-->C()

package main

import (
    "fmt"
    "time"
)

// A首先被a阻塞,A()结束后关闭b,使b可读
func A(a, b chan struct{}) {
    <-a
    fmt.Println("A()!")
    time.Sleep(time.Second)
    close(b)
}

// B首先被a阻塞,B()结束后关闭b,使b可读
func B(a, b chan struct{}) {
    <-a
    fmt.Println("B()!")
    close(b)
}

// C首先被a阻塞
func C(a chan struct{}) {
    <-a
    fmt.Println("C()!")
}

func main() {
    x := make(chan struct{})
    y := make(chan struct{})
    z := make(chan struct{})

    go C(z)
    go A(x, y)
    go C(z)
    go B(y, z)
    go C(z)
    
    // 关闭x,让x可读
    close(x)
    time.Sleep(3 * time.Second)
}

上面的示例中:A goroutine被x阻塞,B goroutine被y阻塞,C goroutine被z阻塞。C依赖的z由B关闭,B依赖的y由A关闭。

如此一来,当main goroutine中的x被关闭后,A()从阻塞中释放,继续执行,关闭y,然后B从阻塞中释放,继续执行,关闭z,C得以释放。由于z被关闭后,z仍然可读,所以多次执行C(z)不会出问题。

A()和B()不能多次执行,因为close()不能操作已被关闭的channel。

注意,上面的channel都是struct{}类型的,整个过程中,x、y、z这3个通道都没有传递数据,而是直接关闭来释放通道,让某些阻塞的goroutine继续执行下去。显然,这里的x、y、z的作用都是"信号通道",用来传递消息。

以上是关于Go入门: 浅谈channel的主要内容,如果未能解决你的问题,请参考以下文章

WEB入门浅谈20

WEB入门浅谈17

浅谈分块——入门

浅谈Ubuntu PowerShell——小白入门教程

Java入门浅谈

CTF入门——隐写术 浅谈(? ?_?)?