<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC] # 簡介 在Go中http包的Server中,每一個請求在都有一個對應的goroutine去處理。請求處理函數通常會啟動額外的goroutine用來訪問后端服務,比如數據庫和RPC服務。用來處理一個請求的goroutine通常需要訪問一些與請求特定的數據,比如終端用戶的身份認證信息,驗證相關的令牌,請求的截止時間。然后系統才能釋放這些goroutine占用的資源。 在Google內部,開發了Context包,專門用來簡化對于處理單個請求的多個goroutine之間與請求域的數據,取消信號,截止時間等相關操作,這些操作可能涉及多個API調用。 ~~~ go get golang.org/x/net/context命令獲取這個包。 ~~~ 注意: 使用時遵循context規則 ~~~ 1. 不要將 Context 放入結構體,Context應該作為第一個參數傳 入,命名為ctx。 2. 即使函數允許,也不要傳入nil的 Context。如果不知道用哪種 Context,可以使用context.TODO()。 3. 使用context的Value相關方法,只應該用于在程序和接口中傳遞 和請求相關數據,不能用它來傳遞一些可選的參數 4. 相同的 Context 可以傳遞給在不同的goroutine;Context 是 并發安全的。 ~~~ Context結構 ~~~ type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} } Deadline() 返回一個time.Time,是當前 Context 的應該結束的時間,ok 表示是否有 deadline Done() 返回一個struct{}類型的只讀 channel Err() 返回 Context 被取消時的錯誤 Value(key interface{}) 是 Context 自帶的 K-V 存儲功能 // Deadline會返回一個超時時間,Goroutine獲得了超時時間后,例如可以對某些io操作設定超時時間。獲取設置的截止時間的意思,第一個返回式是截止時間,到了這個時間點,Context會自動發起取消請求;第二個返回值ok==false時表示沒有設置截止時間,如果需要取消的話,需要調用取消函數進行取消 // Done方法返回一個信道(channel),當Context被撤銷或過期時,該信道是關閉的,即它是一個表示Context是否已關閉的信號。返回一個只讀的chan,類型為struct{},我們在goroutine中,如果該方法返回的chan可以讀取,則意味著parent context已經發起了取消請求,我們通過Done方法收到這個信號后,就應該做清理操作,然后退出goroutine,釋放資源。之后,Err 方法會返回一個錯誤,告知為什么 Context 被取消 // 當Done信道關閉后,Err方法表明Context被撤的原因。如果Context取消的時候,我們就可以得到一個關閉的chan,關閉的chan是可以讀取的,所以只要可以讀取的時候,就意味著收到Context取消的信號了 // Value可以讓Goroutine共享一些數據,當然獲得數據是協程安全的。但使用這些數據的時候要注意同步,比如返回了一個map,而這個map的讀寫則要加鎖。Value方法獲取該Context上綁定的值,是一個鍵值對,所以要通過一個Key才可以獲取對應的值,這個值一般是線程安全的 ~~~ # context實現方法 Context 雖然是個接口,但是并不需要使用方實現,golang內置的context 包,已經幫我們實現了2個方法,一般在代碼中,開始上下文的時候都是以這兩個作為最頂層的parent context,然后再衍生出子context。這些 Context 對象形成一棵樹:當一個 Context 對象被取消時,繼承自它的所有 Context 都會被取消。兩個實現如下: ~~~ var ( background = new(emptyCtx) todo = new(emptyCtx) ) func Background() Context { return background } func TODO() Context { return todo } ~~~ 一個是Background,主要用于main函數、初始化以及測試代碼中,作為Context這個樹結構的最頂層的Context,也就是根Context,它不能被取消。BackGound是所有Context的root,不能夠被cancel 一個是TODO,如果我們不知道該使用什么Context的時候,可以使用這個,但是實際應用中,暫時還沒有使用過這個TODO。 他們兩個本質上都是emptyCtx結構體類型,是一個不可取消,沒有設置截止時間,沒有攜帶任何值的Context。 ~~~ type emptyCtx int func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { return } func (*emptyCtx) Done() <-chan struct{} { return nil } func (*emptyCtx) Err() error { return nil } func (*emptyCtx) Value(key interface{}) interface{} { return nil } ~~~ # Context的繼承 有了如上的根Context,那么是如何衍生更多的子Context的呢?這就要靠context包為我們提供的With系列的函數了。 ~~~ 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 ~~~ 通過這些函數,就創建了一顆Context樹,樹的每個節點都可以有任意多個子節點,節點層級可以有任意多個。 WithCancel函數,傳遞一個父Context作為參數,返回子Context,以及一個取消函數用來取消Context。 WithDeadline函數,和WithCancel差不多,它會多傳遞一個截止時間參數,意味著到了這個時間點,會自動取消Context,當然我們也可以不等到這個時候,可以提前通過取消函數進行取消。 WithTimeout和WithDeadline基本上一樣,這個表示是超時自動取消,是多少時間后自動取消Context的意思。 WithValue函數和取消Context無關,它是為了生成一個綁定了一個鍵值對數據的Context,這個綁定的數據可以通過Context.Value方法訪問到,這是我們實際用經常要用到的技巧,一般我們想要通過上下文來傳遞數據時,可以通過這個方法,如我們需要tarce追蹤系統調用棧的時候。 前三個函數都返回一個取消函數`CancelFunc`,這是一個函數類型,它的定義非常簡單 ~~~go type CancelFunc func() ~~~ 這就是取消函數的類型,該函數可以取消一個Context,以及這個節點Context下所有的所有的Context,不管有多少層級。 # 常用方法 1. 調用Context Done方法取消 ~~~ func Stream(ctx context.Context, out chan<- Value) error { for { v, err := DoSomething(ctx) if err != nil { return err } select { case <-ctx.Done(): return ctx.Err() case out <- v: } } } ~~~ 2. 通過 context.WithValue 來傳值 ~~~ func main() { ctx, cancel := context.WithCancel(context.Background()) valueCtx := context.WithValue(ctx, "key", "add value") go watch(valueCtx) time.Sleep(10 * time.Second) cancel() time.Sleep(5 * time.Second) } func watch(ctx context.Context) { for { select { case <-ctx.Done(): //get value fmt.Println(ctx.Value("key"), "is cancel") return default: //get value fmt.Println(ctx.Value("key"), "int goroutine") time.Sleep(2 * time.Second) } } } ~~~ # 網絡超時請求控制 ~~~ package main import ( "fmt" "sync" "time" "golang.org/x/net/context" ) var ( wg sync.WaitGroup ) func work(ctx context.Context) error { defer wg.Done() for i := 0; i < 1000; i++ { select { case <-time.After(2 * time.Second): fmt.Println("Doing some work ", i) // we received the signal of cancelation in this channel case <-ctx.Done(): fmt.Println("Cancel the context ", i) return ctx.Err() } } return nil } func main() { ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) //釋放資源 defer cancel() fmt.Println("Hey, I'm going to do some work") wg.Add(1) go work(ctx) wg.Wait() fmt.Println("Finished. I'm going home") } ~~~ 4. 截止時間 取消 context.WithDeadline ~~~ package main import ( "context" "fmt" "time" ) func main() { d := time.Now().Add(1 * time.Second) ctx, cancel := context.WithDeadline(context.Background(), d) // Even though ctx will be expired, it is good practice to call its // cancelation function in any case. Failure to do so may keep the // context and its parent alive longer than necessary. defer cancel() select { case <-time.After(2 * time.Second): fmt.Println("oversleep") case <-ctx.Done(): fmt.Println(ctx.Err()) } } ~~~ # 控制多個協程 使用Context控制一個goroutine的例子如上,非常簡單,下面我們看看控制多個goroutine的例子,其實也比較簡單 ~~~go func main() { ctx, cancel := context.WithCancel(context.Background()) go watch(ctx,"【監控1】") go watch(ctx,"【監控2】") go watch(ctx,"【監控3】") time.Sleep(10 * time.Second) fmt.Println("可以了,通知監控停止") cancel() //為了檢測監控過是否停止,如果沒有監控輸出,就表示停止了 time.Sleep(5 * time.Second) } func watch(ctx context.Context, name string) { for { select { case <-ctx.Done(): fmt.Println(name,"監控退出,停止了...") return default: fmt.Println(name,"goroutine監控中...") time.Sleep(2 * time.Second) } } } ~~~ 示例中啟動了3個監控goroutine進行不斷的監控,每一個都使用了Context進行跟蹤,當我們使用`cancel`函數通知取消時,這3個goroutine都會被結束。這就是Context的控制能力,它就像一個控制器一樣,按下開關后,所有基于這個Context或者衍生的子Context都會收到通知,這時就可以進行清理操作了,最終釋放goroutine,這就優雅的解決了goroutine啟動后不可控的問題。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看