成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Golang 中的 Context 包

開發 后端
今天,我們將討論 Go 編程中非常重要的一個主題:context 包。如果你現在覺得它很令人困惑,不用擔心 — 在本文結束時,你將像專家一樣處理 context!

今天,我們將討論 Go 編程中非常重要的一個主題:context 包。如果你現在覺得它很令人困惑,不用擔心 — 在本文結束時,你將像專家一樣處理 context!

想象一下,你在一個主題公園,興奮地準備搭乘一座巨大的過山車。但有個問題:排隊的人非常多,而且公園快要關門,你只有一個小時的時間。你會怎么辦?嗯,你可能會等一會兒,但不會等一個小時,對吧?如果你等了 30 分鐘還沒有到前面,你會離開隊伍去嘗試其他游樂設施。這就是我們所謂的 '超時'。

現在,想象一下,你還在排隊,突然下起了傾盆大雨。過山車的操作員決定關閉過山車。你不會繼續排隊等待根本不會發生的事情,對吧?你會立刻離開隊伍。這就是我們所謂的 '取消'。

在編程世界中,我們經常面臨類似的情況。我們要求程序執行可能需要很長時間或需要因某種原因停止的任務。這就是 context 包發揮作用的地方。它允許我們優雅地處理這些超時和取消。

它是如何工作的

(1) 創建上下文:我們首先創建一個上下文。這就像排隊等待過山車一樣。

ctx := context.Background() // This gives you an empty context

(2) 設置超時:接下來,我們可以在上下文中設置超時。這就好比你決定在排隊多久后放棄并去嘗試其他游樂設施。

ctxWithTimeout, cancel := context.WithTimeout(ctx, time.Second*10) // Wait for 10 seconds
// Don't forget to call cancel when you're done, or else you might leak resources!
defer cancel()

(3) 檢查超時:現在,我們可以使用上下文來檢查是否等待時間太長,是否應該停止我們的任務。這就好比在排隊等待時看看手表。

select {
case <-time.After(time.Second * 15): // This task takes 15 seconds
    fmt.Println("Finished the task")
case <-ctxWithTimeout.Done():
    fmt.Println("We've waited too long, let's move on!") // We only wait for 10 seconds
}

(4) 取消上下文:最后,如果出于某種原因需要停止任務,我們可以取消上下文。這就好比聽到因下雨而宣布過山車關閉。

cancel() // We call the cancel function we got when we created our context with timeout

示例 1:慢速數據庫查詢

想象一下構建一個從數據庫中獲取用戶數據的Web應用程序。有時,數據庫響應較慢,你不希望用戶永遠等下去。在這種情況下,你可以使用帶有超時的上下文。

func getUser(ctx context.Context, id int) (*User, error) {
    // Create a new context that will be cancelled if it takes more than 3 seconds
    ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
    defer cancel()

    // Assume db.QueryRowContext is a function that executes a SQL query and returns a row
    row := db.QueryRowContext(ctx, "SELECT name FROM users WHERE id = ?", id)

    var name string
    if err := row.Scan(&name); err != nil {
        return nil, err
    }

    return &User{Name: name}, nil
}

在這個示例中,如果數據庫查詢花費超過3秒的時間,上下文將被取消,db.QueryRowContext 應返回一個錯誤。

示例 2:網頁抓取

假設你正在編寫一個用于從網站抓取數據的程序。然而,該網站有時響應較慢,或者根本不響應。你可以使用上下文來防止你的程序陷入困境。

func scrapeWebsite(ctx context.Context, url string) (*html.Node, error) {
    // Create a new context that will be cancelled if it takes more than 5 seconds
    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()

    // Create a request with the context
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
    if err != nil {
        return nil, err
    }

    // Execute the request
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    // Parse the response body as HTML
    return html.Parse(resp.Body), nill
}

在這個示例中,如果從網站獲取數據超過5秒,上下文將被取消,http.DefaultClient.Do 應該返回一個錯誤。

示例3:長時間運行的任務

假設你有一個執行長時間運行任務的程序,但你希望能夠在程序接收到關閉信號時停止任務。這在一個 Web 服務器中可能會很有用,當關閉時必須停止提供請求并進行清理。

func doTask(ctx context.Context) {
    for {
        select {
        case <-time.After(1 * time.Second):
            // The task is done, we're ready to exit
            fmt.Println("Task is done")
            return
        case <-ctx.Done():
            // The context was cancelled from the outside, clean up and exit
            fmt.Println("Got cancel signal, cleaning up")
            return
        }
    }
}

func main() {
    // Create a new context
    ctx, cancel := context.WithCancel(context.Background())

    // Start the task in a goroutine
    go doTask(ctx)

    // Wait for a shutdown signal
    <-getShutdownSignal()

    // Cancel the context, which will stop the task
    cancel()

    // Wait for a bit to allow the task to clean up
    time.Sleep(1 * time.Second)
}

在這個示例中,當程序接收到關閉信號時,它會取消上下文,這會導致 doTask 在 <-ctx.Done() 上接收到信號。

示例4:HTTP 服務器

假設你正在構建一個處理傳入請求的 HTTP 服務器。一些請求可能需要很長時間來處理,你希望設置一個最長處理時間限制。

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
    defer cancel()

    // Simulate a long-running operation
    select {
    case <-time.After(3 * time.Second):
        w.Write([]byte("Operation finished."))
    case <-ctx.Done():
        w.Write([]byte("Operation timed out."))
    }
})

http.ListenAndServe(":8080", nil)

在這個示例中,如果操作需要超過2秒的時間,上下文將被取消,并且服務器將響應“操作超時”。

示例5:同步多個 Goroutines

假設你正在編寫一個程序,使用 Goroutines 并發執行多個任務。如果其中一個任務失敗,你希望取消所有其他任務。

func doTask(ctx context.Context, id int) {
    select {
    case <-time.After(time.Duration(rand.Intn(4)) * time.Second):
        fmt.Printf("Task %v finished.\n", id)
    case <-ctx.Done():
        fmt.Printf("Task %v cancelled.\n", id)
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())

    for i := 1; i <= 5; i++ {
        go doTask(ctx, i)
    }

    // Cancel the context after 2 seconds
    time.Sleep(2 * time.Second)
    cancel()

    // Give the tasks some time to finish up
    time.Sleep(1 * time.Second)
}

在這個示例中,當上下文被取消時,仍在運行的任何任務都將收到 <-ctx.Done(),從而允許它們進行清理并退出。

仍然在嘗試理解嗎?

當我第一次接觸上下文時,我感到非常困惑,我提出了一個問題,即如果 select 前面的命令花費太長時間,那么我們永遠無法檢測到 取消,這是一個合理的問題。因此,我準備了另一個示例來詳細解釋這種情況。

package main

import (
 "context"
 "fmt"
 "math/rand"
 "time"
)

func expensiveCalculation(ctx context.Context, resultChan chan<- int) {
 // Simulate a long-running calculation
 rand.Seed(time.Now().UnixNano())
 sleepTime := time.Duration(rand.Intn(20)+1) * time.Second
 fmt.Printf("Calculation will take %s to complete\n", sleepTime)

 time.Sleep(sleepTime)

 select {
 case <-ctx.Done():
  // Context was cancelled, don't write to the channel
  return
 default:
  // Write the result to the channel
  resultChan <- 42 // replace with your actual calculation result
 }
}

func main() {
 // Create a context that will be cancelled after 10 seconds
 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 defer cancel() // The cancel should be deferred so resources are cleaned up

 resultChan := make(chan int)

 // Start the expensive calculation in a separate goroutine
 go expensiveCalculation(ctx, resultChan)

 // Wait for either the result or the context to be done
 select {
 case res := <-resultChan:
  // Got the result
  fmt.Printf("Calculation completed with result: %d\n", res)
 case <-ctx.Done():
  // Context was cancelled
  fmt.Println("Calculation cancelled")
 }
}

time.Sleep(sleepTime) 命令是阻塞的,將暫停 goroutine 的執行,直到指定的持續時間已過。這意味著 select 語句不會被執行,直到休眠時間已經過去。

然而,上下文的取消與 goroutine 內的執行是獨立的。如果上下文的截止時間被超過或其 cancel() 函數被調用,它的 Done() 通道將被關閉。

在主 goroutine 中,您有另一個 select 語句,它將立即檢測上下文的 Done() 通道是否已關閉,并在不等待 expensiveCalculation goroutine 完成休眠的情況下打印 **"Calculation cancelled"**。

也就是說,expensiveCalculation goroutine 將在休眠后繼續執行,它將在嘗試寫入 resultChan 之前檢查上下文是否已被取消。如果已被取消,它將立即返回。這是為了避免潛在的死鎖,如果沒有其他goroutine從 resultChan 讀取。

如果需要昂貴的計算(在本例中由 time.Sleep 模擬)在取消時立即停止,您必須設計計算以周期性地檢查上下文是否已取消。這通常在需要將計算分解為較小部分的情況下使用循環。如果計算不能分解,并需要一次運行完畢,那么很遺憾,在 Go 中無法提前停止它。

責任編輯:趙寧寧 來源: 技術的游戲
相關推薦

2023-05-12 09:40:53

ContextGolang

2023-05-15 08:50:58

ContextGolang

2021-04-28 09:02:48

Golang語言Context

2023-10-24 16:03:34

GoGolang

2023-08-03 08:48:07

Golang接口

2023-11-27 15:02:37

BytesGolang

2023-08-02 09:07:27

Golangio 包

2023-10-18 08:22:38

BufioGolang

2023-09-06 09:10:04

Golang字符串

2023-11-07 09:02:07

Golangbytes

2023-09-07 07:35:54

GolangBufio

2023-09-04 08:17:37

Golangstrings 包

2023-08-28 17:16:51

Golangio 包

2023-09-05 08:22:44

Golangstrings 包

2024-01-18 09:07:04

Errors函數Golang

2023-10-10 08:57:44

Golangbufio

2023-11-03 08:53:15

StrconvGolang

2023-10-07 09:08:32

Golangbufio

2014-08-07 10:06:05

ndroidContext

2023-08-08 14:51:29

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 人人干人人玩 | 国产福利视频网站 | 视频精品一区 | 久www| 久久一区二区免费视频 | 天堂网中文字幕在线观看 | 国产精品一区二区三区免费观看 | 一级一级毛片免费看 | 亚洲一区视频 | 国产高清性xxxxxxxx | 亚洲一区二区三区观看 | 久久另类 | 91国内精品久久 | 中文字幕 国产 | 日本成人毛片 | 免费一级做a爰片久久毛片潮喷 | 欧美区在线 | 欧美一区二区三区一在线观看 | 日本久久网 | 97精品国产一区二区三区 | 一区二区三区在线观看免费视频 | 在线观看免费毛片 | 成人毛片在线观看 | a级网站 | 亚洲日本激情 | www久久久| 国产偷录叫床高潮录音 | 一级黄色淫片 | 天天av天天好逼 | 国产精品亚洲一区二区三区在线 | 黄网站在线播放 | 亚洲精品视频在线播放 | 国产一区二区在线视频 | 涩爱av一区二区三区 | 久久精彩视频 | 国产综合一区二区 | 国产精品视频免费看 | 亚洲视频免费在线看 | 91高清在线观看 | 国产午夜精品一区二区三区四区 | 国产成人精品一区二区三区网站观看 |