golang 中的channel

Channel是什么

在Go语言中,Channel即指通道类型。有时也用它来直接指代可以传递某种类型的值的通道。

类型表示法

chan T

关键字chan代表了通道类型的关键字,T则代表了该通道类型的元素类型。

例如:type IntChan chan int 别名类型IntChan代表了元素类型为int的通道类型。我们可以直接声明一个chan int类型的变量:var IntChan chan int,在被初始化后,变量IntChan就可以被用来传递int类型的元素值了。

chan<- T

只能被用来发送值, <-表示发送操作符

<-chan T

接收通道值, <-表示接收操作符

值表示法

属性和基本操作

  • 基于通道的通讯是在多个Goroutine之间进行同步的重要手段。而针对通道的操作本身也是同步的。
  • 在同一时刻,仅有一个Goroutine能向一个通道发送元素值
  • 同时也仅有一个Goroutine能从它那里接收元素值。
  • 通道相当于一个FIFO先进先出的消息队列。
  • 通道中的元素值都具有原子性。它们是不可被分割的。通道中的每一个元素都只可能被某一个Goroutine接收。已被接收的元素值会立刻被从通道中删除。

初始化通道

make(chan int, 10)

~ 表达式初始化了一个通道类型的值。传递给make函数的第一个参数表明此值的具体类型是元素类型为int的通道类型,而第二个参数则指该值在同一时刻最多可以容纳10个元素值。

package main
import (
    "fmt"
)

type Person struct {
    Name string
    Age  uint8
    Address Addr
}

type Addr struct{
    city string
    district string
}

func main(){
    persionChan := make(chan Person,1)

    p1 := Person{"Harry",32,Addr{"Shanxi","Xian"}}
    fmt.Printf("P1 (1): %v\n",p1)

    persionChan <- p1

    p1.Address.district = "shijingshan"
    fmt.Printf("P2 (2): %v\n",p1)

    p1_copy := <-persionChan
    fmt.Printf("p1_copy: %v\n",p1_copy)
}

go test.go 运行结果

P1 (1): {Harry 32 {Shanxi Xian}}
P2 (2): {Harry 32 {Shanxi shijingshan}}
p1_copy: {Harry 32 {Shanxi Xian}}

通道中的元素值丝毫没有受到外界的影响。这说明了,在发送过程中进行的元素值属于完全复制。这也保证了我们使用通道传递的值的不变性。

关闭通道

close(strChan)

我们应该先明确一点:无论怎么样都不应该在接收端关闭通道。因为在那里我们无法判断发送端是否还会向该通道发送元素值。

package main

import (
    "fmt"
    "time"
)

func main(){
    ch := make(chan int, 5)
    sign := make(chan int, 2)

    go func() {
        for i :=0;i<5;i++ {
            ch <- i
            time.Sleep(1 * time.Second)
        }
        close(ch)
        fmt.Println("The channel is closed.")
        sign <- 0
    }()

    go func() {
        for {
            e, ok := <-ch
            fmt.Printf("%d (%v)\n", e,ok)
            if !ok {
                break
            }
            time.Sleep(2 * time.Second)
        }
        fmt.Println("Done.")
        sign <- 1
    }()
    <- sign
    <- sign
}

    运行结果:
    0 (true)
    1 (true)
    2 (true)
    The channel is closed.
    3 (true)
    4 (true)
    0 (false)
    Done.

运行时系统并没有在通道ch被关闭之后立即把false作为相应接收操作的第二个结果,而是等到接收端把已在通道中的所有元素值都接收到之后才这样做。这确保了在发送端关闭通道的安全性。