Go并发编程4-Context


关于Context:

在WebServer中,每个请求都对应一个goroutine,同时还会启动若干goroutine去处理后端业务,如数据库RPC服务身份认证信息等。
当一个请求被 取消或超时 时,所有用来处理该请求的goroutine都应该迅速退出,然后系统才能释放这些goroutine占用的资源。


Context解析:

  • context包括运行环境、现场和快照等。context 包主要是用来处理多个goroutine 之间共享数据,及多个 goroutine 的管理。

  • Done() 返回一个只能接受数据的channel类型,当该context关闭或者超时时间到了的时候,该channel就会有一个取消信号。

  • Context通常最为函数的第一个参数使用,且不要传递nil(可使用context.Backgroundcontext.TODO)。

  • Value() 方法允许 Context 对象携带request作用域的数据,该数据必须是线程安全的。

  • Context的核心是围绕**取消子协程**展开的,上下文核心成员是Done() <-chan struct{}

  • context.Background()emptyCtx类型,它实现了Context接口,WithCancel等是责任链模式。

  • context.TODO()context.Background() 等价,都是一个emptyCtx类型。

  • 通过向下传递time.Duitiontime.Timecancel回调函数控制取消的时机。


1. 取消

func main() {
   // 传统方式:读变量
   cancelByVar()
   fmt.Println("==================")

   // 传统方式:通信
   cancelByChannel()
   fmt.Println("==================")

   // 推荐:上下文通道
   cancelByContext()
   fmt.Println("==================")

   // 扩展案例
   withCancelEx()
}

// 方式一:变量控制取消操作
func cancelByVar() {
   var cancel = false
   go func() {
      for {
         if cancel {
            break
         }
         fmt.Println("时间心跳:", time.Now().Second())
         time.Sleep(time.Second)
      }
   }()
   time.Sleep(time.Second * 3)
   cancel = true
}

// 方式二:channel控制取消操作
func cancelByChannel() {
   var cancel = make(chan struct{})
   go func(c chan struct{}) {
   OVER:
      for {
         select {
         case <-c:
            break OVER
         default:
         }

         fmt.Println("时间心跳:", time.Now().Second())
         time.Sleep(time.Second)
      }
   }(cancel)

   time.Sleep(time.Second * 3)
   cancel <- struct{}{}
}

// 方式三:context手动控制取消
func cancelByContext() {
   process := func(ctx context.Context) {
   OVER:
      for {
         fmt.Println("时间心跳:", time.Now().Second())
         time.Sleep(time.Second)
         select {
         case <-ctx.Done(): // 向上报告
            break OVER
         default:
         }
      }
   }

   ctx, cancel := context.WithCancel(context.Background())
   go process(ctx)

   time.Sleep(time.Second * 3)
   cancel() //三秒后通知取消
}

// WithCancel案例
func withCancelEx() {
   fetch := func(ctx context.Context) <-chan int {
      trans := make(chan int) //单通道必须经过中转
      go func() {
      OVER:
         for {
            select {
            case <-ctx.Done():
               break OVER
               // (模拟)发送当前时间秒
            case trans <- time.Now().Second():
               time.Sleep(time.Second)
            }
         }
      }()
      return trans
   }

   ctx, cancel := context.WithCancel(context.Background())
   for n := range fetch(ctx) {
      fmt.Println("时间心跳:", n)
      if n%5 == 0 {
         break
      }
   }
   cancel()
}

2. 超时

func main() {
   withTimeout()
}

func withTimeout() {
   var wg sync.WaitGroup
   wg.Add(1)

   process := func(ctx context.Context) {
   OVER:
      for {
         time.Sleep(time.Second)
         select {
         case er := <-ctx.Done():
            fmt.Println(er) //取消的原因
            break OVER
         default:
            fmt.Println("时间心跳:", time.Now().Second())
         }
      }
      wg.Done()
   }

   ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
   go process(ctx)

   _ = cancel //cancel是个方法,这也可以手动提前结束
   wg.Wait()
}

3. 过期

func main() {
   dead := time.Now().Add(3 * time.Second) //截止时间
   ctx, cancel := context.WithDeadline(context.Background(), dead)
   defer cancel()

   // 会员有效期检测
   select {
   case <-time.After(1 * time.Second):
      fmt.Println("欢迎光临,尽情畅玩吧...")
   case <-ctx.Done():
      fmt.Println("会员失效,请充值...", ctx.Err())
   }
   fmt.Println("over")
}

4. 传值

func main() {
   process := func(ctx context.Context) {
      trace_id, ok := ctx.Value("trace_id").(int)
      if ok {
         fmt.Println("trace_id =", trace_id)
      }

      session, _ := ctx.Value("session").(string)
      fmt.Println("session =", session)
   }

   // 上下文传值
   ctx := context.WithValue(context.Background(), "trace_id", 1001)
   ctx = context.WithValue(ctx, "session", "86011WQGJ")
   process(ctx)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值