一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Golang 中的 Context 包

 技術(shù)的游戲 2023-11-12 發(fā)布于廣東

簡介

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

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

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

在編程世界中,我們經(jīng)常面臨類似的情況。我們要求程序執(zhí)行可能需要很長時(shí)間或需要因某種原因停止的任務(wù)。這就是 context 包發(fā)揮作用的地方。它允許我們優(yōu)雅地處理這些超時(shí)取消。

它是如何工作的

創(chuàng)建上下文:我們首先創(chuàng)建一個(gè)上下文。這就像排隊(duì)等待過山車一樣。

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

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

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()

檢查超時(shí):現(xiàn)在,我們可以使用上下文來檢查是否等待時(shí)間太長,是否應(yīng)該停止我們的任務(wù)。這就好比在排隊(duì)等待時(shí)看看手表。

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
}

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

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

示例 1:慢速數(shù)據(jù)庫查詢

想象一下構(gòu)建一個(gè)從數(shù)據(jù)庫中獲取用戶數(shù)據(jù)的Web應(yīng)用程序。有時(shí),數(shù)據(jù)庫響應(yīng)較慢,你不希望用戶永遠(yuǎn)等下去。在這種情況下,你可以使用帶有超時(shí)的上下文。

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
}

在這個(gè)示例中,如果數(shù)據(jù)庫查詢花費(fèi)超過3秒的時(shí)間,上下文將被取消,db.QueryRowContext 應(yīng)返回一個(gè)錯(cuò)誤。

示例 2:網(wǎng)頁抓取

假設(shè)你正在編寫一個(gè)用于從網(wǎng)站抓取數(shù)據(jù)的程序。然而,該網(wǎng)站有時(shí)響應(yīng)較慢,或者根本不響應(yīng)。你可以使用上下文來防止你的程序陷入困境。

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
}

在這個(gè)示例中,如果從網(wǎng)站獲取數(shù)據(jù)超過5秒,上下文將被取消,http.DefaultClient.Do 應(yīng)該返回一個(gè)錯(cuò)誤。

示例3:長時(shí)間運(yùn)行的任務(wù)

假設(shè)你有一個(gè)執(zhí)行長時(shí)間運(yùn)行任務(wù)的程序,但你希望能夠在程序接收到關(guān)閉信號(hào)時(shí)停止任務(wù)。這在一個(gè) Web 服務(wù)器中可能會(huì)很有用,當(dāng)關(guān)閉時(shí)必須停止提供請求并進(jìn)行清理。

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)
}

在這個(gè)示例中,當(dāng)程序接收到關(guān)閉信號(hào)時(shí),它會(huì)取消上下文,這會(huì)導(dǎo)致 doTask 在 <-ctx.Done() 上接收到信號(hào)。

示例4:HTTP 服務(wù)器

假設(shè)你正在構(gòu)建一個(gè)處理傳入請求的 HTTP 服務(wù)器。一些請求可能需要很長時(shí)間來處理,你希望設(shè)置一個(gè)最長處理時(shí)間限制。

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)

在這個(gè)示例中,如果操作需要超過2秒的時(shí)間,上下文將被取消,并且服務(wù)器將響應(yīng)“操作超時(shí)”。

示例5:同步多個(gè) Goroutines

假設(shè)你正在編寫一個(gè)程序,使用 Goroutines 并發(fā)執(zhí)行多個(gè)任務(wù)。如果其中一個(gè)任務(wù)失敗,你希望取消所有其他任務(wù)。

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)
}

在這個(gè)示例中,當(dāng)上下文被取消時(shí),仍在運(yùn)行的任何任務(wù)都將收到 <-ctx.Done(),從而允許它們進(jìn)行清理并退出。

仍然在嘗試?yán)斫鈫幔?/h2>

當(dāng)我第一次接觸上下文時(shí),我感到非常困惑,我提出了一個(gè)問題,即如果 select 前面的命令花費(fèi)太長時(shí)間,那么我們永遠(yuǎn)無法檢測到 取消,這是一個(gè)合理的問題。因此,我準(zhǔn)備了另一個(gè)示例來詳細(xì)解釋這種情況。

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 的執(zhí)行,直到指定的持續(xù)時(shí)間已過。這意味著 select 語句不會(huì)被執(zhí)行,直到休眠時(shí)間已經(jīng)過去。

然而,上下文的取消與 goroutine 內(nèi)的執(zhí)行是獨(dú)立的。如果上下文的截止時(shí)間被超過或其 cancel() 函數(shù)被調(diào)用,它的 Done() 通道將被關(guān)閉。

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

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

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

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多

    亚洲中文字幕视频在线观看| 麻豆果冻传媒一二三区| 风韵人妻丰满熟妇老熟女av| 国产精品视频第一第二区| 日本在线 一区 二区| 亚洲熟女国产熟女二区三区| 一区二区三区日韩经典| 日本一本不卡免费视频| 日本在线不卡高清欧美| 欧美精品久久99九九| 欧美日韩国产一级91| 亚洲欧美中文字幕精品| 亚洲熟女国产熟女二区三区| 粉嫩国产美女国产av| 精品国产日韩一区三区| 欧美一区二区不卡专区| 欧美午夜不卡在线观看| 欧美一区二区日韩一区二区| 99国产高清不卡视频| 亚洲欧洲一区二区中文字幕| 国产日产欧美精品视频| 日韩女优精品一区二区三区| 亚洲欧美一二区日韩高清在线| 少妇被粗大进猛进出处故事| 亚洲欧洲成人精品香蕉网| 久久国产精品熟女一区二区三区| 国产乱久久亚洲国产精品| 国产对白老熟女正在播放| 人妻一区二区三区在线| 欧美一区日韩一区日韩一区| 中文字幕人妻综合一区二区| 欧美不雅视频午夜福利| 色婷婷人妻av毛片一区二区三区| 精品精品国产欧美在线| 亚洲综合日韩精品欧美综合区| 欧美精品中文字幕亚洲| 亚洲一区二区精品国产av| 麻豆国产精品一区二区| 日韩欧美三级中文字幕| 日韩三极片在线免费播放| 国产精品一级香蕉一区|