91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

golang通過context控制并發的應用場景實現

發布時間:2020-08-30 04:53:41 來源:腳本之家 閱讀:380 作者:只是一個id 欄目:編程語言

golang 里出現多 goroutine 的場景很常見, 最常用的兩種方式就是 WaitGroup 和 Context, 今天我們了解一下 Context 的應用場景

使用場景

場景一: 多goroutine執行超時通知

并發執行的業務中最常見的就是有協程執行超時, 如果不做超時處理就會出現一個僵尸進程, 這累計的多了就會有一陣手忙腳亂了, 所以我們要在源頭上就避免它們

看下面這個示例:

package main

import (
 "context"
 "fmt"
 "time"
)

/**
同一個content可以控制多個goroutine, 確保線程可控, 而不是每新建一個goroutine就要有一個chan去通知他關閉
有了他代碼更加簡潔
*/

func main() {
 fmt.Println("run demo \n\n\n")
 demo()
}

func demo() {
 ctx, cancel := context.WithTimeout(context.Background(), 9*time.Second)
 go watch(ctx, "[線程1]")
 go watch(ctx, "[線程2]")
 go watch(ctx, "[線程3]")

 index := 0
 for {
  index++
  fmt.Printf("%d 秒過去了 \n", index)
  time.Sleep(1 * time.Second)
  if index > 10 {
   break
  }
 }

 fmt.Println("通知停止監控")
 // 其實此時已經超時, 協程已經提前退出
 cancel()

 // 防止主進程提前退出
 time.Sleep(3 * time.Second)
 fmt.Println("done")
}

func watch(ctx context.Context, name string) {
 for {
  select {
  case <-ctx.Done():
   fmt.Printf("%s 監控退出, 停止了...\n", name)
   return
  default:
   fmt.Printf("%s goroutine監控中... \n", name)
   time.Sleep(2 * time.Second)
  }
 }
}

使用 context.WithTimeout() 給文本流設置一個時間上限, 結合 for+select 去接收消息. 當執行超時,或手動關閉都會給 <-ctx.Done() 發送消息,而且所有使用同一個 context 都會收到這個通知, 免去了一個一個通知的繁瑣代碼

場景二: 類似web服務器中的session

比如在php中(沒用swoole擴展), 一個請求進來, 從 $_REQUEST $_SERVER 能獲取到的是有關這一條請求的所有信息, 哪怕是使用全局變量也是給這一個請求來服務的, 是線程安全的

但是 golang 就不一樣了, 因為程序本身就能起一個 web sever, 因此就不能隨便使用全局變量了, 不然就是內存泄露警告. 但是實際業務當中需要有一個類似session 的東西來承載單次請求的信息, 舉一個具體的例子就是: 給每次請求加一個 uniqueID 該如何處理? 有了這個 uniqueID, 請求的所有日志都能帶上它, 這樣排查問題的時候方便追蹤一次請求發生了什么

如下:

func demo2() {
 pCtx, pCancel := context.WithCancel(context.Background())
 pCtx = context.WithValue(pCtx, "parentKey", "parentVale")
 go watch(pCtx, "[父進程1]")
 go watch(pCtx, "[父進程2]")

 cCtx, cCancel := context.WithCancel(pCtx)
 go watch(cCtx, "[子進程1]")
 go watch(cCtx, "[子進程2]")
 fmt.Println(pCtx.Value("parentKey"))
 fmt.Println(cCtx.Value("parentKey"))

 time.Sleep(10 * time.Second)
 fmt.Println("子進程關閉")
 cCancel()
 time.Sleep(5 * time.Second)
 fmt.Println("父進程關閉")
 pCancel()

 time.Sleep(3 * time.Second)
 fmt.Println("done")
}

最開始的 context.WithCancel(context.Background()) 中 context.Background() 就是一個新建的 context, 利用 context 能繼承的特性, 可以將自己的程序構建出一個 context 樹, context 執行 cancel() 將影響到當前 context 和子 context, 不會影響到父級.

同時 context.WithValue 也會給 context 帶上自定義的值, 這樣 uniqueID 就能輕松的傳遞了下去, 而不是一層層的傳遞參數, 改func什么的

對于 context 很值得參考的應用有:

  • Gin
  • logrus

Context 相關 func 和接口

繼承 context 需要實現如下四個接口

type Context interface {
 Deadline() (deadline time.Time, ok bool)

 Done() <-chan struct{}

 Err() error

 Value(key interface{}) interface{}
}

當使用的時候不需要實現接口, 因為官方包里已經基于 emptyCtx 實現了一個, 調用方法有

var (
 background = new(emptyCtx)
 todo  = new(emptyCtx)
)

// 這個是最初始的ctx, 之后的子ctx都是繼承自它
func Background() Context {
 return background
}

// 不清楚context要干嘛, 但是就得有一個ctx的用這個
func TODO() Context {
 return todo
}

繼承用的函數

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
  • WithCancel 返回一個帶 cancel 函數的ctx,
  • WithDeadline 在到達指定時間時自動執行 cancel()
  • WithTimeout 是 WithDeadline的殼子, 區別就是這個函數是多少時間過后執行 cancel
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
 return WithDeadline(parent, time.Now().Add(timeout))
}

WithValue 繼承父類ctx時順便帶上一個值

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

灵璧县| 邵阳市| 广河县| 桓台县| 西宁市| 越西县| 南乐县| 邢台市| 扬州市| 竹北市| 菏泽市| 凌海市| 淳化县| 宝应县| 东源县| 抚州市| 辽阳市| 竹山县| 锡林浩特市| 筠连县| 子长县| 深水埗区| 敦化市| 米泉市| 临海市| 炎陵县| 长宁县| 迁安市| 鸡泽县| 保定市| 稻城县| 梓潼县| 济阳县| 襄垣县| 大厂| 蓝山县| 高密市| 上犹县| 鄂托克旗| 甘泉县| 寻甸|