就是让Goroutine以更优雅的方式退出
package main
import (
"context"
"fmt"
"sync"
"time"
)
/*标题: Context*/
func genNumbers(ctx context.Context) <-chan int {
dst := make(chan int)
n := 1
go func() {
for {
select {
case <-ctx.Done(): // 结束了就返回
return
case dst <- n: // 能往里面放数就放
n++
}
}
}()
return dst
}
// context.WithCancel()测试
func cancelTest() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // 取完数之后要调用 cancel
for n := range genNumbers(ctx) {
fmt.Println(n)
if n == 5 {
break
}
}
}
// context.WithDeadline()测试
// 当截止时刻到达时会被迫cancel()
func deadlineTest() {
d := time.Now().Add(5 * time.Millisecond)
ctx, cancel := context.WithDeadline(context.Background(), d)
// 尽管ctx会过期, 但是在任何情况下调用cancel函数都是有必要的
// 如果不这么做, 可能会使上下文及父类存活时间超过必要的时间
defer cancel()
select {
//After waits for the duration to elapse and then sends the current time on the returned channel.
case <-time.After(time.Second): // After等待一段时间过去然后发送
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err())
}
}
// context.WithValue()测试
func withValueTest() {
type TraceCode string
var wg sync.WaitGroup
worker := func(ctx context.Context) {
key := TraceCode("TRACE_CODE")
traceCode, ok := ctx.Value(key).(string) // 类型断言
if !ok {
fmt.Println("invalid trace code")
}
LOOP:
for {
fmt.Printf("worker, trace code:%s\n", traceCode)
time.Sleep(time.Millisecond * 10) // 假设正常连接数据库耗时10秒
select {
case <-ctx.Done():
break LOOP
default:
}
}
fmt.Println("Worker done!")
wg.Done()
}
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
ctx = context.WithValue(ctx, TraceCode("TRACE_CODE"), "12512312234")
wg.Add(1)
go worker(ctx)
time.Sleep(time.Second * 5)
cancel()
wg.Wait()
fmt.Println("over")
}
func main() {
withValueTest()
}