文章目录
  1. 1. channel
    1. 1.1. 写值
    2. 1.2. 读值
    3. 1.3. 关闭
    4. 1.4. for循环读
  2. 2. 生产者消费者模型
  3. 3. 总结

从生产者消费者模型探究回顾golang channel注意事项; 实例探究no buffer及buffer channel;

channel

golang channel 分为无缓冲channel及缓冲channel, 具体体现在创建channel时是否制定channel size,

1
2
3
4
5
//no buffer channel
ch := make(chan int)

//buffer channel
ch := make(chan int bufSize)
  • 无缓冲channel 默认channel大小为1, 写入一个值后需要被读取之后才能继续写入, 否则写阻塞;
  • 缓冲channel 的大小是初始时的bufSize, 可连续写入bufSize值, 然后等待读取, 当len(channel) < bufSize时才可以继续写入, 否则写阻塞;
  • channel 中没有值时 则读阻塞;
  • channel 常用在同步, pipe, 无锁设计等场景中;

写值

1
ch <- in
  • 非写阻塞时可以写入值;

读值

1
2
3
out := <-ch 

out, ok := <- ch
  • 非读阻塞时可以读取值;
  • 当ok 为false时, 表示channel已被关闭;

关闭

1
close(ch)
  • 只要当写channel写入完毕后则应立即关闭channel; 而读channel则可以不需要处理;

更多参考Range and close

for循环读

1
2
3
for x := range ch {
//do something with x
}
  • 当使用for range 从管道读取数据的时候,管道没有数据,那么循环会阻塞,只有当管道被关闭的时候,for 循环才会结束

生产者消费者模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//producer_consumer.go
const (
N = 100000
)

func Producer(out chan<- int) {
for i := 1; i < N; i++ {
out <- i
}
close(out)
}

func Consumer(in <-chan int, out chan<- int) {
for x := range in {
out <- x
}
close(out)
}

测试case

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
//producer_consumer_test.go
func producerconsumer(in chan int, out chan int) {
go Producer(in)
go Consumer(in, out)

for x := range out {
_ = x
}
}

func TestNoBufferChan(t *testing.T) {
in, out := make(chan int), make(chan int)
producerconsumer(in, out)
}

func TestBufferChan(t *testing.T) {
bufLen := 100
in, out := make(chan int, bufLen), make(chan int, bufLen)
producerconsumer(in, out)
}

func BenchmarkNoBufferChan(b *testing.B) {
for i := 0; i < b.N; i++ {
in, out := make(chan int), make(chan int)
producerconsumer(in, out)
}
}

func BenchmarkBufferChan(b *testing.B) {
for i := 0; i < b.N; i++ {
bufLen := 100
in, out := make(chan int, bufLen), make(chan int, bufLen)
producerconsumer(in, out)
}
}

output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
➜  make bench
[Benchmark] running Benchmark
=== RUN TestCloseWriteChanException
--- PASS: TestCloseWriteChanException (2.01s)
=== RUN TestCloseReadChanException
--- PASS: TestCloseReadChanException (1.00s)
=== RUN TestNoBufferChan
--- PASS: TestNoBufferChan (0.09s)
=== RUN TestBufferChan
--- PASS: TestBufferChan (0.02s)
goos: darwin
goarch: amd64
pkg: github.com/researchlab/experiments/channel
BenchmarkNoBufferChan-8 20 84429085 ns/op
BenchmarkBufferChan-8 50 22295688 ns/op
PASS
ok github.com/researchlab/experiments/channel 6.045s

更多分析及完整源码可参见 github-channel

总结

  • 回顾了channel两种类型(no buffer/buffer channel);
  • 只有写channel需要close, 而读channel 则可以不需要关心; 需要注意channel 在多个goroutine中的close问题;
  • channel 是非常常用的一种结果, 常见于业务同步, buffer pipe, timeout, 无锁设计等场景中;

作者署名:朴实的一线攻城狮
本文标题:从生产者消费者模型深入学习golang channel
本文出处:http://researchlab.github.io/2018/11/08/producer-consumer-go-channel/
版权声明:本文由Lee Hong创作和发表,采用署名(BY)-非商业性使用(NC)-相同方式共享(SA)国际许可协议进行许可,转载请注明作者及出处, 否则保留追究法律责任的权利。

@全栈炼狱之路

关注微信公众号 @全栈炼狱之路

总访问:
总访客: