有缓冲 (边写边消费)
当channel 容量 和 消费生产容量相同时,不会发生阻塞,我们看这里
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan int, 3)
fmt.Println("len c is ", len(c), "cap c is ", cap(c))
go func() {
defer fmt.Println("sub go is dnoe!")
for i := 0; i < 3; i++ {
c <- i
fmt.Println("sub-go is running sending element", i, "Len c is", len(c), "cap c is ", cap(c))
}
}()
time.Sleep(2 * time.Second)
for i := 0; i < 3; i++ {
num := <-c
fmt.Println("num is ", num)
}
fmt.Println("main done!")
}
Lux@Luuuxury Go% go build channel.go
Lux@Luuuxury Go% ./channel
len c is 0 cap c is 3
sub-go is sending element 0 Len c is 1 cap c is 3
sub-go is sending element 1 Len c is 2 cap c is 3
sub-go is sending element 2 Len c is 3 cap c is 3
sub go is dnoe!
num is 0
num is 1
num is 2
main done!
如果我们把容量 c := make(chan int, 3)
改成 2,运行结果如下(每个人的输出顺序可能不一样)
len c is 0 cap c is 2
sub-go is sending element 0 Len c is 1 cap c is 2
sub-go is sending element 1 Len c is 2 cap c is 2
num is 0
num is 1
sub-go is sending element 2 Len c is 2 cap c is 2
sub go is dnoe!
num is 2
main done!
无缓冲 (一次性写,一次性读)
func main() {
c := make(chan int, 5)
fmt.Println("len c is", len(c), "cap c is ", cap(c))
go func() {
for i := 0; i < 5; i++ {
c <- i
}
close(c)
}()
for {
if data, ok := <-c; ok {
fmt.Println(data)
} else {
break
}
}
}
运行
len c is 0 cap c is 5
0
1
2
3
4
这里主线程和子协呈程做呼应的其实是 close 和 ok
, 添加close ,当子协程运行完后close,后通过ok判断 channel 是否还有数据。如果我们把close去掉,运行
len c is 0 cap c is 5
0
1
2
3
4
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
当channel 为空时候,又由于消费者的持续读取,发生死锁
当然这里也可以 简化 if data, ok := <-c; ok {
,也可以使用range来迭代操作channel,如下
for data := range c {
fmt.Println(data)
}
效果也是一样的
其他参考
类比操作系统里面的PV操作
mutex = 1 作为互斥量 ; empty = n 自定义容量 ; full 初始值为 0
生产者:
P(empty)
P(mutex)
放入
V(mutex)
V(full)
消费者
P(full)
P(mutex)
取出
P(mutex)
V(empty)
如果互斥在前,同步在后, 当前容器放满了(只能等待消费了), 那么会出现, P生产(mutex), --> P生产(empty) - 此时没有空位, --> 生产者阻塞,转到消费者 P消费(mutex), 被生产者互斥阻塞, 所以, 必须同步P在前, 互斥P在后