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作为相应接收操作的第二个结果,而是等到接收端把已在通道中的所有元素值都接收到之后才这样做。这确保了在发送端关闭通道的安全性。