GO语言中的channel | 青训营笔记

108 阅读2分钟

这是我参与「第五届青训营 」笔记创作活动的第2天

这篇文章主要记录一些学习青训营课程【GO语言的实战案例】过程中遇到不会的知识点。

channel信道

channel信道类似一个管道,主要用于在goroutine之间传递信息,使得goroutine能够有秩序地配合工作。

使用符号<-可以向channel发送数据或者从channel中读取数据,为了搞懂代码逻辑,我们可以将符号<-可以看作是数据的流向,<一端是数据接收方,-一端是数据发送方,可以看一下下面的例子:

// ch是一个可读可写的channel
//1、从channel中读取数据
v:=<-ch
//2、向channel中发送数据data
v<-data

根据是否可读可写可以将channel分为三种类型:

  1. chan T 可读可写
  2. <-chan T 只读
  3. chan<- T 只写

其中T为信道中的数据类型。

如何创建一个channel?可以使用make来创建:

ch:=make(channel_type,capacity=0)

默认情况下channel的容量为0,从channel中读取数据时会阻塞等待,直到其他goroutine向此channel发送数据。

channel的容量>0时,说明这是一个带缓存的信道,若缓存为空,则从channel中读取数据将阻塞等待,直到信道中有数据;若缓存已满,则向channel中发送数据将阻塞等待,直到信道中有数据被取出。

我们可以使用close(ch)关闭信道。

值得注意的是,当一个channel被关闭后,使用<-ch读取数据时不会被阻塞,若信道中还有数据仍然可以读出,若信道中的数据已经被读取完,则返回的是channel类型的零值,下面有个课程中遇到的例子

// dest表示从socks5代理服务器到目标服务器的连接
// conn表示从客户端到socks代理服务器的连接
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
    io.Copy(dest, conn)
    cancel()
}()
go func() {
    io.Copy(conn, dest)
    cancel()
}()
<-ctx.Done()

上面这段代码是socks5代理服务器的部分逻辑,当socks5代理服务器与目标服务器建立连接后,socks5代理服务器将conn的输入流(Reader)复制到dest的输出流(Writer),将dest的输入流(Reader)复制到conn的输出流(Writer)。

最后一行代码使用<-ctx.Done()等待客户端与服务端交互完毕,当ctxcancel函数被调用后,会调用ctx.Done()将会返回一个关闭的channel,使得这行代码不再阻塞。