channel 语法
var ch chan T. 声明 channel
make(chan T) 和 make(chan T, size) 声 不带容量 和 带容量的 channel
ch <- data 发送消息到 channel 里面
val := <- ch 从 channel 里面读取数据
close(ch) 关闭 channel
channel 的 close 问题
close 不是幂等的
当被 close 之后
-
继续写入数据,会 panic
-
从 channcel 读取的数据 会读到 0 值 。 val, ok := <- ch ; ok = 0
-
再次 close 会 panic
在实践中坚持一个原则 : 谁创建的 channel 谁来关, 可以避免很多 channel 没有正确关闭的问题
channel Loop
我们可以使用 for range 进行读取 channel 的数据
但是需要注意 close 如果不 close channel 会 panic . 因为接收方会一直接受数据,但是发送方没有数据了 所以会一直阻塞
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
func LoopChannel() {
ch := make(chan int)
go func() {
for i := 0; i < 3; i++ {
ch <- i
time.Sleep(time.Second)
}
close(ch)
}()
for val := range ch {
fmt.Println(val)
}
fmt.Println("done")
}
|
channel & 阻塞
-
如果接受者读不到数据,就会阻塞
-
如果发送者写不了消息,就会阻塞
有缓冲和 无缓冲的 channel
无缓冲 Channel :
缓冲 Channel :
有缓冲的写法 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
func BufferChannel() {
ch := make(chan int, 3)
defer func() {
close(ch)
}()
for i := 1; i <= 3; i++ {
ch <- i
}
for i := 1; i <= 3; i++ {
val := <-ch
fmt.Println(val)
}
}
|
无缓冲的写法 :
-
要写进行接收,然后才可以进行发送
-
并且必须同时处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
func NoBufferChannel() {
ch := make(chan int)
defer func() {
close(ch)
}()
for i := 1; i <= 3; i++ {
go func() {
val := <-ch
fmt.Println(val)
}()
ch <- i
}
//for i := 1; i <= 3; i++ {// panic
// val := <-ch
// fmt.Println(val)
//}
}
|
channel 与 select
select-case 用于控制从不同的 channel 中读写数据 ,只运行一次
语法
1
2
3
4
5
|
select {
case ch <- val: // 写入
case val := <- ch2 // 读取
default :
}
|
-
每一个 case 都可以是读取或者是写入数据到 channel
-
没有 default 分支的时候, select 就会阻塞
-
如果有多个分支同时满足 会随机执行一个
-
case 后面不需要break
应用
输出 0 - 100
使用 goroutine, 交替 输出 0 - 100
写法如下 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
func Print100() {
time := time2.Now()
even := make(chan int)
odd := make(chan int)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
for i := 0; i <= N; i += 2 {
<-even
fmt.Print("p1:", i, "\n")
if i == N {
wg.Done()
close(odd)
return
}
odd <- i
}
}()
go func() {
for i := 1; i <= N; i += 2 {
<-odd
fmt.Print("p2:", i, "\n")
if i == N-1 {
wg.Done()
close(even)
return
}
even <- i
}
}()
even <- 0
wg.Wait()
fmt.Println("cost:", time2.Now().Sub(time))
}
|
我们也可以使用一个 channel 进行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
func print100() {
time := time2.Now()
ch := make(chan int)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
for {
val, ok := <-ch
if !ok {
return
}
fmt.Print("p1:", val, "\n")
if val == N {
wg.Done()
close(ch)
return
}
val += 1
ch <- val
}
}()
go func() {
for {
val, ok := <-ch
if !ok {
return
}
fmt.Print("p2:", val, "\n")
if val == N {
wg.Done()
close(ch)
return
}
val += 1
ch <- val
}
}()
ch <- 0
wg.Wait()
fmt.Println("time:", time2.Now().Sub(time))
}
|
在 1e7 的数据下 :
2 个协程 : cost: 9.692623833s
顺序遍历 : cost : 6.417589125s
失败的写法
我这里使用使用 for 直接进行输出,但是这里没办法保证是 交替 输出
因为我们这里只是保证了能够存入 channel 而没办法 fmt.Print
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
func NewPrinter2() *Printer2 {
p := &Printer2{
ch: make(chan int),
}
return p
}
func (p *Printer2) Print() {
for val := range p.ch {
fmt.Println("p2:", val)
}
}
func SelectChange100() {
p1 := NewPrinter1()
p2 := NewPrinter2()
defer func() {
close(p1.ch)
close(p2.ch)
}()
go p1.Print()
go p2.Print()
for i := 1; i < 100; i++ {
if i%2 == 1 {
p1.ch <- i
} else {
p2.ch <- i
}
}
}
|