写法核心在有注释的部分
读管道
判断读管道中是否还有数据
注意,此写法不能用于判断管道是否关闭
package main
import (
"fmt"
)
func main() {
ch := make(chan string, 10)
ch <- "hello"
ch <- "world"
// 判断数据状态
if val, open := <-ch; !open {
fmt.Println("channel is closed")
} else {
fmt.Println(val, open)
}
close(ch)
if val, open := <-ch; !open {
fmt.Println("channel is closed")
} else {
fmt.Println(val, open)
}
}
取出管道中的所有数据,直到管道关闭
package main
import (
"context"
"fmt"
"time"
)
func main() {
ch := make(chan string)
ctx, cancel := context.WithCancel(context.Background())
go func(cancel func()) {
time.Sleep(5 * time.Second)
cancel()
close(ch)
}(cancel)
go func(ctx context.Context) {
num := 0
for {
select {
case <-ctx.Done():
fmt.Println("done")
return
default:
ch <- fmt.Sprintf("hello %d", num)
num++
time.Sleep(1 * time.Second)
}
}
}(ctx)
// for val := range ch 会取出 ch 中的所有数据,其退出条件是,ch 被关闭且 ch 中的数据被全部取出
for val := range ch {
fmt.Println(val)
}
time.Sleep(10 * time.Second)
}
写管道
使用 content 来管理状态
package main
import (
"context"
"fmt"
"time"
)
func main() {
ch := make(chan string)
ctx, cancel := context.WithCancel(context.Background())
go func(cancel func()) {
time.Sleep(5 * time.Second)
// 关闭管道之前先关闭 content
cancel()
close(ch)
}(cancel)
go func(ctx context.Context) {
num := 0
for {
//此处来去控制写管道
select {
case <-ctx.Done():
fmt.Println("done")
return
default:
ch <- fmt.Sprintf("hello %d", num)
num++
time.Sleep(1 * time.Second)
}
}
}(ctx)
for val := range ch {
fmt.Println(val)
}
time.Sleep(10 * time.Second)
}
panic+recover
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
go func() {
time.Sleep(5 * time.Second)
close(ch)
}()
go func() {
// 让它 panic 后恢复,借此来判断管道关闭
// 此写法需要单独抽象成一个函数,其中最好不可能有其他的 panic
defer func() {
if err := recover(); err != nil {
fmt.Println("recover", err)
}
}()
num := 0
for {
ch <- fmt.Sprintf("hello %d", num)
num++
time.Sleep(1 * time.Second)
}
}()
for val := range ch {
fmt.Println(val)
}
time.Sleep(10 * time.Second)
}