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

Go 1.13 相比 Go 1.12 有哪些值得注意的改動(dòng)?

開發(fā) 前端
Go 1.13 中 ??sync?? 包的改進(jìn)提升了常用同步原語的性能,并使 ??sync.Pool?? 在高并發(fā)和頻繁 GC 的場(chǎng)景下表現(xiàn)更加穩(wěn)定和高效。

https://go.dev/doc/go1.13

Go 1.13 帶來了一系列語言、工具鏈、運(yùn)行時(shí)和標(biāo)準(zhǔn)庫(kù)的改進(jìn)。以下是一些值得開發(fā)者關(guān)注的重點(diǎn)改動(dòng):

  • 語言特性 : 引入了更統(tǒng)一和現(xiàn)代化的數(shù)字字面量表示法,包括二進(jìn)制 (0b)、八進(jìn)制 (0o) 前綴、十六進(jìn)制浮點(diǎn)數(shù)、數(shù)字分隔符 (_) 等,并取消了移位操作計(jì)數(shù)必須為無符號(hào)數(shù)的限制。
  • Go Modules 與 Go 命令 : GO111MODULE=auto 在檢測(cè)到 go.mod 文件時(shí)將默認(rèn)啟用模塊感知模式,即使在 GOPATH 內(nèi);引入 GOPRIVATE 等環(huán)境變量更好地管理私有模塊和代理配置;go get -u 的更新邏輯有所調(diào)整;go 命令增加了如 go env -w、go version <executable>、go build -trimpath 等新功能。
  • Runtime 運(yùn)行時(shí) : 優(yōu)化了切片越界時(shí)的 panic 信息,使其包含越界索引和切片長(zhǎng)度;defer 的性能在大多數(shù)場(chǎng)景下提升了約 30%;運(yùn)行時(shí)會(huì)更積極地將不再使用的內(nèi)存歸還給操作系統(tǒng)。
  • 錯(cuò)誤處理 : 正式引入了 錯(cuò)誤包裝(error wrapping)機(jī)制,通過 fmt.Errorf 的新 %w 動(dòng)詞和 errors 包新增的 UnwrapIs、As 函數(shù),可以創(chuàng)建和檢查包含原始錯(cuò)誤上下文的錯(cuò)誤鏈。
  • sync 包 : 通過內(nèi)聯(lián)優(yōu)化,sync.Mutex、sync.RWMutex 和 sync.Once 在非競(jìng)爭(zhēng)情況下的性能得到提升(鎖操作約 10%,Once.Do 約 2 倍);sync.Pool 對(duì) GC 暫停時(shí)間(STW)的影響減小,并且能在 GC 后保留部分對(duì)象,減少 GC 后的冷啟動(dòng)開銷。

下面是一些值得展開的討論:

語言特性:更現(xiàn)代化的數(shù)字字面量與有符號(hào)位移

Go 1.13 在語言層面引入了幾項(xiàng)旨在提升代碼可讀性和易用性的改進(jìn)。

首先是數(shù)字字面量的增強(qiáng):

  • 二進(jìn)制字面量 (Binary Literals) : 使用前綴 0b 或 0B 表示二進(jìn)制整數(shù),例如 0b1011 代表十進(jìn)制的 11。
  • 八進(jìn)制字面量 (Octal Literals) : 使用前綴 0o 或 0O 表示八進(jìn)制整數(shù),例如 0o660 代表十進(jìn)制的 432。需要注意的是,舊式的以 0 開頭的八進(jìn)制表示法(如 0660)仍然有效,但推薦使用新的 0o 前綴以避免歧義。
  • 十六進(jìn)制浮點(diǎn)數(shù)字面量 (Hexadecimal Floating-point Literals) : 允許使用 0x 或 0X 前綴表示浮點(diǎn)數(shù)的尾數(shù)部分,但必須帶有一個(gè)以 p 或 P 開頭的二進(jìn)制指數(shù)。例如 0x1.0p-2 表示 ,即 0.25。
  • 虛數(shù)字面量后綴 (Imaginary Literals) : 虛數(shù)后綴 i 現(xiàn)在可以用于任何整數(shù)或浮點(diǎn)數(shù)字面量(二進(jìn)制、八進(jìn)制、十進(jìn)制、十六進(jìn)制),如 0b1011i0o660i3.14i、0x1.fp+2i
  • 數(shù)字分隔符 (Digit Separators) : 可以使用下劃線 _ 來分隔數(shù)字,以提高長(zhǎng)數(shù)字的可讀性,例如 1_000_000、0b_1010_0110 或 3.1415_9265。下劃線可以出現(xiàn)在任意兩個(gè)數(shù)字之間,或者前綴和第一個(gè)數(shù)字之間。
package main

import "fmt"

func main() {
    binaryNum := 0b1101 // 13
    octalNum := 0o755   // 493
    hexFloat := 0x1.Fp+2 // 1.9375 * 2^2 = 7.75
    largeNum := 1_000_000_000
    complexNum := 0xAp1 + 1_2i // (10 * 2^1) + 12i = 20 + 12i

    fmt.Println(binaryNum)
    fmt.Println(octalNum)
    fmt.Println(hexFloat)
    fmt.Println(largeNum)
    fmt.Println(complexNum)
    // 13
    // 493
    // 7.75
    // 1000000000
    // (20+12i)
}

其次,Go 1.13 取消了移位操作(<< 和 >>)的移位計(jì)數(shù)(右操作數(shù))必須是無符號(hào)整數(shù)的限制?,F(xiàn)在可以直接使用有符號(hào)整數(shù)作為移位計(jì)數(shù)。

這消除了之前為了滿足類型要求而進(jìn)行的許多不自然的 uint 轉(zhuǎn)換。

package main

import "fmt"

func main() {
    var signedShift int = 2
    var value int64 = 100

    // Go 1.12 及之前: 需要顯式轉(zhuǎn)換為 uint
    // shiftedValueOld := value << uint(signedShift)

    // Go 1.13 及之后: 可以直接使用 signed int
    shiftedValueNew := value << signedShift

    // fmt.Println(shiftedValueOld) // 輸出 400
    fmt.Println(shiftedValueNew) // 輸出 400

    var negativeShift int = -2 // 負(fù)數(shù)移位也是允許的,但行為依賴于具體實(shí)現(xiàn)和架構(gòu),通常不建議
    fmt.Println(value >> negativeShift) // 行為可能非預(yù)期,輸出可能為 0 或 panic,取決于 Go 版本和具體情況
}
400
panic: runtime error: negative shift amount

goroutine 1 [running]:
main.main()
        /home/piperliu/code/playground/main.go:19 +0x85
exit status 2

需要注意的是,要使用這些新的語言特性,你的項(xiàng)目需要使用 Go Modules,并且 go.mod 文件中聲明的 Go 版本至少為 1.13。你可以手動(dòng)編輯 go.mod 文件,或者運(yùn)行 go mod edit -go=1.13 來更新。

Go Modules 與 Go 命令:模塊化體驗(yàn)改進(jìn)與工具增強(qiáng)

Go 1.13 在 Go Modules 和 go 命令行工具方面帶來了重要的改進(jìn),旨在簡(jiǎn)化開發(fā)流程和模塊管理。

模塊行為與環(huán)境變量

  • GO111MODULE=auto 的行為變化:現(xiàn)在,只要當(dāng)前工作目錄或其任何父目錄包含 go.mod 文件,auto 設(shè)置就會(huì)激活模塊感知模式。這意味著即使項(xiàng)目位于傳統(tǒng)的 GOPATH/src 目錄下,只要存在 go.mod,go 命令也會(huì)優(yōu)先使用模塊模式。這極大地簡(jiǎn)化了從 GOPATH 遷移到 Modules 的過程以及混合管理兩種模式項(xiàng)目的場(chǎng)景。
  • 新的環(huán)境變量 GOPRIVATE、GONOPROXY、GONOSUMDB:為了更好地處理私有模塊(例如公司內(nèi)部的代碼庫(kù)),引入了 GOPRIVATE 環(huán)境變量。它用于指定一組不應(yīng)通過公共代理 (GOPROXY) 下載或通過公共校驗(yàn)和數(shù)據(jù)庫(kù) (GOSUMDB) 驗(yàn)證的模塊路徑模式(支持通配符)。GOPRIVATE 會(huì)作為 GONOPROXY 和 GONOSUMDB 的默認(rèn)值,提供更細(xì)粒度的控制。
  • GOPROXY 默認(rèn)值與配置:GOPROXY 環(huán)境變量現(xiàn)在支持逗號(hào)分隔的代理 URL 列表,以及特殊值 direct(表示直接連接源倉(cāng)庫(kù))。其默認(rèn)值更改為 https://proxy.golang.org,directgo 命令會(huì)按順序嘗試列表中的每個(gè)代理,直到成功下載或遇到非 404/410 錯(cuò)誤。
  • GOSUMDB:用于指定校驗(yàn)和數(shù)據(jù)庫(kù)的名稱和可選的公鑰及 URL。默認(rèn)值為 sum.golang.org。如果模塊不在主模塊的 go.sum 文件中,go 命令會(huì)查詢 GOSUMDB 以驗(yàn)證下載模塊的哈希值,確保依賴未被篡改??梢栽O(shè)置為 off 來禁用此檢查。

對(duì)于無法訪問公共代理或校驗(yàn)和數(shù)據(jù)庫(kù)的環(huán)境(如防火墻內(nèi)),可以使用 go env -w 命令設(shè)置全局默認(rèn)值:

# 僅直接從源倉(cāng)庫(kù)下載,不使用代理
go env -w GOPROXY=direct

# 禁用校驗(yàn)和數(shù)據(jù)庫(kù)檢查
go env -w GOSUMDB=off

# 配置私有模塊路徑 (示例)
go env -w GOPRIVATE=*.corp.example.com,github.com/my-private-org/*

go get 行為調(diào)整

  • go get -u 的更新范圍:在模塊模式下,go get -u (不帶包名參數(shù)時(shí))現(xiàn)在只更新當(dāng)前目錄包的直接和間接依賴。這與 GOPATH 模式下的行為更一致。如果要更新 go.mod 中定義的所有依賴(包括測(cè)試依賴)到最新版本,應(yīng)使用 go get -u all
  • go get -u <package> 的更新范圍:當(dāng)指定包名時(shí),go get -u <package> 會(huì)更新指定的包及其導(dǎo)入的包所在的模塊,而不是這些模塊的所有傳遞依賴。
  • @patch 版本后綴:go get 支持了新的 @patch 版本后綴。例如 go get example.com/mod@patch 會(huì)將 example.com/mod 更新到當(dāng)前主版本和次版本下的最新補(bǔ)丁版本。
  • @upgrade 和 @latest@upgrade 明確要求將模塊升級(jí)到比當(dāng)前更新的版本(如果沒有新版本則保持不變,防止意外降級(jí)預(yù)發(fā)布版本)。@latest 則總是嘗試獲取最新的發(fā)布版本,無論當(dāng)前版本如何。

版本校驗(yàn)增強(qiáng)

go 命令在處理模塊版本時(shí)增加了更嚴(yán)格的校驗(yàn):

  • +incompatible 版本:如果一個(gè)倉(cāng)庫(kù)使用了 +incompatible 標(biāo)記(通常用于 Modules 出現(xiàn)之前的 v2+ 版本),go 命令現(xiàn)在會(huì)驗(yàn)證該版本對(duì)應(yīng)的代碼樹中 不能 包含 go.mod 文件。
  • 偽版本 (Pseudo-versions):對(duì)形如 vX.Y.Z-yyyymmddhhmmss-abcdefabcdef 的偽版本格式進(jìn)行了更嚴(yán)格的校驗(yàn),確保版本前綴、時(shí)間戳和 commit 哈希與版本控制系統(tǒng)的元數(shù)據(jù)一致。如果 go.mod 中有無效的偽版本,通??梢酝ㄟ^將其簡(jiǎn)化為 commit 哈希(如 require example.com/mod abcdefabcdef)然后運(yùn)行 go mod tidy 或 go list -m all 來自動(dòng)修正。對(duì)于傳遞依賴中的無效版本,可以使用 replace 指令強(qiáng)制替換為有效的版本或 commit 哈希。

其他 go 命令改進(jìn)

  • go env -w 和 -u:允許設(shè)置和取消設(shè)置 go 命令環(huán)境變量的用戶級(jí)默認(rèn)值,存儲(chǔ)在用戶配置目錄下的 go/env 文件中。
  • go version <executable> 或 <directory>:可以查看 Go 二進(jìn)制文件是用哪個(gè) Go 版本編譯的(使用 -m 標(biāo)志可查看嵌入的模塊信息),或查看目錄及其子目錄下所有 Go 二進(jìn)制文件的版本信息。
  • go build -trimpath:一個(gè)新的構(gòu)建標(biāo)志,用于從編譯出的二進(jìn)制文件中移除所有本地文件系統(tǒng)路徑信息,有助于提高構(gòu)建的可復(fù)現(xiàn)性。

錯(cuò)誤處理:官方錯(cuò)誤包裝(Error Wrapping)機(jī)制

Go 1.13 引入了一個(gè)重要的原生機(jī)制來處理錯(cuò)誤: 錯(cuò)誤包裝 (error wrapping) 。這個(gè)特性解決了長(zhǎng)期以來在 Go 中處理錯(cuò)誤時(shí)的一個(gè)痛點(diǎn):如何在添加上下文信息的同時(shí),保留底層原始錯(cuò)誤以便進(jìn)行程序化檢查。

問題背景

在 Go 1.13 之前,當(dāng)一個(gè)函數(shù)遇到來自底層調(diào)用的錯(cuò)誤,并想添加更多關(guān)于當(dāng)前操作的上下文信息時(shí),通常的做法是使用 fmt.Errorf 創(chuàng)建一個(gè)新的錯(cuò)誤字符串,包含原始錯(cuò)誤的信息(通過 %v 或 err.Error())。

// Go 1.13 之前的常見做法
func readFile(path string) error {
    f, err := os.Open(path)
    if err != nil {
        // 創(chuàng)建了新錯(cuò)誤,丟失了原始 err 的類型信息 (如 *os.PathError)
        return fmt.Errorf("failed to open file %q: %v", path, err)
    }
    // ...
    defer f.Close()
    return nil
}

func checkPermission() {
    err := readFile("/path/to/protected/file")
    // 無法直接判斷 err 是否是權(quán)限錯(cuò)誤,因?yàn)樵嫉?os.ErrPermission 信息丟失了
    // if err == os.ErrPermission { ... } // 這通常行不通
}

這種方式的問題在于,返回的錯(cuò)誤是一個(gè)全新的 string 類型的錯(cuò)誤(由 fmt.Errorf 創(chuàng)建),原始錯(cuò)誤的類型信息(例如 *os.PathError)和值(例如 os.ErrNotExist)丟失了。調(diào)用者無法方便地檢查錯(cuò)誤的根本原因,例如判斷它是不是一個(gè)特定的錯(cuò)誤類型或哨兵錯(cuò)誤值(sentinel error)。

Go 1.13 的解決方案:%wUnwrapIsAs

Go 1.13 通過以下方式解決了這個(gè)問題:

fmt.Errorf 的 %w 動(dòng)詞

fmt.Errorf 函數(shù)增加了一個(gè)新的格式化動(dòng)詞 %w。當(dāng)使用 %w 來格式化一個(gè)錯(cuò)誤時(shí),fmt.Errorf 會(huì)創(chuàng)建一個(gè)新的錯(cuò)誤,這個(gè)新錯(cuò)誤不僅包含了格式化后的字符串信息,還 包裝 (wrap) 了原始的錯(cuò)誤。這個(gè)包裝后的錯(cuò)誤會(huì)實(shí)現(xiàn)一個(gè) Unwrap() error 方法,該方法返回被包裝的原始錯(cuò)誤。

package main

import (
    "errors"
    "fmt"
    "os"
    "io/fs" // fs.ErrNotExist 在 Go 1.16 引入,之前是 os.ErrNotExist
)

// queryDatabase 模擬數(shù)據(jù)庫(kù)查詢錯(cuò)誤
var ErrDBConnection = errors.New("database connection failed")

func queryDatabase(query string) error {
    // 模擬連接失敗
    return ErrDBConnection
}

// handleRequest 處理請(qǐng)求,調(diào)用數(shù)據(jù)庫(kù)查詢
func handleRequest(req string) error {
    err := queryDatabase(req)
    if err != nil {
        // 使用 %w 包裝原始錯(cuò)誤 ErrDBConnection
        return fmt.Errorf("failed to handle request '%s': %w", req, err)
    }
    return nil
}

// readFileWithErrorWrapping 示例
func readFileWithErrorWrapping(path string) error {
    _, err := os.Open(path)
    if err != nil {
        // 使用 %w 包裝 os.Open 返回的錯(cuò)誤
        return fmt.Errorf("error opening file %s: %w", path, err)
    }
    return nil
}


func main() {
    // 場(chǎng)景1:檢查特定的哨兵錯(cuò)誤
    err := handleRequest("SELECT * FROM users")
    if err != nil {
        fmt.Printf("Original error: %v\n", err) // 輸出包含包裝信息

        // 使用 errors.Is 檢查錯(cuò)誤鏈中是否包含 ErrDBConnection
        if errors.Is(err, ErrDBConnection) {
            fmt.Println("Error check passed: The root cause is ErrDBConnection.")
        } else {
            fmt.Println("Error check failed: The root cause is NOT ErrDBConnection.")
        }
    }

    fmt.Println("---")

    // 場(chǎng)景2:檢查特定的錯(cuò)誤類型并獲取其值
    errFile := readFileWithErrorWrapping("non_existent_file.txt")
    if errFile != nil {
        fmt.Printf("Original file error: %v\n", errFile)

        // 使用 errors.As 檢查錯(cuò)誤鏈中是否有 *fs.PathError 類型
        // 并將該類型的錯(cuò)誤值賦給 pathErr
        var pathErr *fs.PathError
        if errors.As(errFile, &pathErr) {
            fmt.Printf("Error check passed: It's a PathError.\n")
            fmt.Printf("  Operation: %s\n", pathErr.Op)
            fmt.Printf("  Path: %s\n", pathErr.Path)
            fmt.Printf("  Underlying error: %v\n", pathErr.Err) // 底層具體錯(cuò)誤
        } else {
            fmt.Println("Error check failed: It's NOT a PathError.")
        }

        // 也可以用 errors.Is 檢查底層的哨兵錯(cuò)誤
        if errors.Is(errFile, fs.ErrNotExist) {
   fmt.Println("Further check: The underlying error IS fs.ErrNotExist.")
        }
    }
}

errors.Unwrap(err error) error

這個(gè)函數(shù)接收一個(gè)錯(cuò)誤 err。如果 err 實(shí)現(xiàn)了 Unwrap() error 方法,errors.Unwrap 會(huì)調(diào)用它并返回其結(jié)果(即被包裝的那個(gè)錯(cuò)誤)。如果 err 沒有包裝其他錯(cuò)誤,則返回 nil。這允許你手動(dòng)地逐層解開錯(cuò)誤鏈。

  1. errors.Is(err error, target error) bool

這是檢查錯(cuò)誤鏈的首選方式。它會(huì)遞歸地解開 err 的錯(cuò)誤鏈(通過調(diào)用 Unwrap),檢查鏈中的任何一個(gè)錯(cuò)誤是否 等于target 哨兵錯(cuò)誤值(使用 == 比較)。如果找到匹配項(xiàng),返回 true。這對(duì)于檢查是否發(fā)生了某個(gè)已知的、預(yù)定義的錯(cuò)誤(如 io.EOFsql.ErrNoRows, 或自定義的哨兵錯(cuò)誤)非常有用。

errors.As(err error, target interface{}) bool

這也是檢查錯(cuò)誤鏈的首選方式。它會(huì)遞歸地解開 err 的錯(cuò)誤鏈,檢查鏈中的任何一個(gè)錯(cuò)誤是否可以賦值給target 指向的類型。如果找到匹配項(xiàng),它會(huì)將該錯(cuò)誤值賦給 targettarget 必須是一個(gè)指向錯(cuò)誤類型接口或具體錯(cuò)誤類型的指針),并返回 true。這對(duì)于檢查錯(cuò)誤是否屬于某個(gè)特定類型,并希望獲取該類型錯(cuò)誤的具體字段信息(如 *os.PathError 的 Op 和 Path 字段)非常有用。

最佳實(shí)踐

  • 當(dāng)你想給一個(gè)錯(cuò)誤添加上下文,并且希望調(diào)用者能夠檢查或響應(yīng)原始錯(cuò)誤時(shí),使用 fmt.Errorf 的 %w 動(dòng)詞進(jìn)行包裝。
  • 當(dāng)你只想記錄錯(cuò)誤信息,不關(guān)心調(diào)用者是否需要檢查原始錯(cuò)誤時(shí),繼續(xù)使用 %v 或 err.Error()。
  • 優(yōu)先使用 errors.Is 來檢查錯(cuò)誤鏈中是否包含特定的哨兵錯(cuò)誤值。
  • 優(yōu)先使用 errors.As 來檢查錯(cuò)誤鏈中是否包含特定類型的錯(cuò)誤,并獲取該錯(cuò)誤的值以訪問其字段。
  • 避免直接調(diào)用 Unwrap 方法,除非你有特殊需要逐層處理錯(cuò)誤鏈。errors.Is 和 errors.As 通常是更健壯和方便的選擇。

錯(cuò)誤包裝機(jī)制極大地增強(qiáng)了 Go 的錯(cuò)誤處理能力,使得構(gòu)建更健壯、更易于調(diào)試和維護(hù)的程序成為可能。

sync 包:性能優(yōu)化與 sync.Pool 改進(jìn)

Go 1.13 對(duì) sync 包中的一些常用同步原語進(jìn)行了性能優(yōu)化,并改進(jìn)了 sync.Pool 的行為。

鎖和 Once 的性能提升

sync.Mutex(互斥鎖)、sync.RWMutex(讀寫鎖)和 sync.Once(保證函數(shù)只執(zhí)行一次)是非?;A(chǔ)且常用的同步工具。

  • sync.Mutex : 用于保護(hù)臨界區(qū),確保同一時(shí)間只有一個(gè) goroutine 可以訪問共享資源。
var mu sync.Mutex
var counter int

func increment() {
 mu.Lock() // 獲取鎖
 defer mu.Unlock() // 保證釋放鎖
 counter++
}
  • sync.RWMutex : 允許多個(gè)讀取者同時(shí)訪問資源,但寫入者必須獨(dú)占訪問。適用于讀多寫少的場(chǎng)景。
var rwMu sync.RWMutex
var config map[string]string

func getConfig(key string) string {
 rwMu.RLock() // 獲取讀鎖
 defer rwMu.RUnlock() // 釋放讀鎖
 return config[key]
}

func setConfig(key, value string) {
 rwMu.Lock() // 獲取寫鎖
 defer rwMu.Unlock() // 釋放寫鎖
 config[key] = value
}
  • sync.Once : 用于確保某個(gè)初始化操作或其他需要只執(zhí)行一次的動(dòng)作,在并發(fā)環(huán)境下確實(shí)只執(zhí)行一次。
var once sync.Once
var serviceInstance *Service

func GetService() *Service {
 once.Do(func() {
  // 初始化操作,只會(huì)在首次調(diào)用 Do 時(shí)執(zhí)行
  serviceInstance = &Service{}
  serviceInstance.init()
 })
 return serviceInstance
}

在 Go 1.13 中,這些原語的 快速路徑 (fast path) (即沒有發(fā)生鎖競(jìng)爭(zhēng)或 Once.Do 已經(jīng)被執(zhí)行過的情況)被 內(nèi)聯(lián) (inlined) 到了調(diào)用者的代碼中。這意味著在最常見、性能最關(guān)鍵的非競(jìng)爭(zhēng)場(chǎng)景下,調(diào)用這些方法的開銷顯著降低。根據(jù)官方說明,在 amd64 架構(gòu)下:

  • Mutex.LockMutex.UnlockRWMutex.LockRWMutex.RUnlock 的非競(jìng)爭(zhēng)情況性能提升高達(dá) 10%。
  • Once.Do 在非首次執(zhí)行時(shí)(即 once 已經(jīng)被觸發(fā)后)的速度提升了大約 2 倍。

sync.Pool 的改進(jìn)

sync.Pool 是一個(gè)用于存儲(chǔ)和復(fù)用臨時(shí)對(duì)象的技術(shù),主要目的是減少內(nèi)存分配次數(shù)和 GC 壓力,尤其適用于那些需要頻繁創(chuàng)建和銷毀、生命周期短暫的對(duì)象(如網(wǎng)絡(luò)連接的緩沖區(qū)、編解碼器的狀態(tài)對(duì)象等)。

var bufferPool = sync.Pool{
    New: func() interface{} {
        // New 函數(shù)用于在 Pool 為空時(shí)創(chuàng)建新對(duì)象
        fmt.Println("Allocating new buffer")
        return make([]byte, 4096) // 例如創(chuàng)建一個(gè) 4KB 的緩沖區(qū)
    },
}

func handleConnection(conn net.Conn) {
    // 從 Pool 獲取一個(gè) buffer
    buf := bufferPool.Get().([]byte)

    // 使用 buffer ...
    n, err := conn.Read(buf)
    // ...

    // 將 buffer 放回 Pool 以便復(fù)用
    // 注意:放回前最好清理一下 buffer 內(nèi)容(如果需要)
    // e.g., buf = buf[:0] or zero out parts of it
    bufferPool.Put(buf)
}

Go 1.13 對(duì) sync.Pool 做了兩項(xiàng)重要改進(jìn):

  • 減少對(duì) GC STW (Stop-The-World) 暫停時(shí)間的影響 :在之前的版本中,如果 sync.Pool 中緩存了大量對(duì)象,清理這些對(duì)象(尤其是在 GC 期間)可能會(huì)對(duì) STW 暫停時(shí)間產(chǎn)生比較明顯的影響。Go 1.13 優(yōu)化了 sync.Pool 的內(nèi)部實(shí)現(xiàn),使得即使池中對(duì)象很多,對(duì) GC 暫停時(shí)間的影響也顯著減小。
  • 跨 GC 保留部分對(duì)象 :這是 sync.Pool 行為的一個(gè)重大變化。在 Go 1.13 之前, 每次 GC 運(yùn)行時(shí),sync.Pool 中的所有緩存對(duì)象都會(huì)被無條件清除 。這意味著每次 GC 之后,如果程序繼續(xù)請(qǐng)求對(duì)象,Pool 會(huì)變空,導(dǎo)致大量調(diào)用 New 函數(shù)來重新填充緩存,這可能在 GC 后造成短暫的性能抖動(dòng)(分配和 GC 壓力增加)。

從 Go 1.13 開始,sync.Pool可以在 GC 之后保留一部分之前緩存的對(duì)象 。它使用了一個(gè)兩階段的緩存機(jī)制,主緩存池仍然會(huì)在 GC 時(shí)被清理,但會(huì)有一個(gè)備用(受害者)緩存池保留上一次 GC 清理掉的對(duì)象,供本次 GC 后使用。這樣,GC 之后 Pool 不再是完全空的,可以更快地提供緩存對(duì)象,減少了對(duì) New 的調(diào)用頻率,從而平滑了 GC 后的性能表現(xiàn),降低了負(fù)載峰值。

使用 sync.Pool 的注意事項(xiàng)(結(jié)合 1.13 改進(jìn))

  • sync.Pool 仍然適用于臨時(shí)對(duì)象的復(fù)用,以減少分配和 GC 壓力。
  • 由于對(duì)象現(xiàn)在可能跨 GC 保留,從 Pool 中 Get 到的對(duì)象可能包含上次使用時(shí)殘留的數(shù)據(jù)。因此,在使用前對(duì)其進(jìn)行必要的 重置或清理 變得更加重要(例如,對(duì)于 []byte,使用 buf = buf[:0];對(duì)于結(jié)構(gòu)體,清零關(guān)鍵字段)。
  • Pool 保留對(duì)象的能力并不意味著你可以用它來管理需要精確生命周期控制的資源(如文件句柄、網(wǎng)絡(luò)連接),這些資源通常需要顯式的 Close 方法。
  • 雖然跨 GC 保留對(duì)象減少了冷啟動(dòng)開銷,但也意味著 Pool 可能會(huì)持有內(nèi)存更長(zhǎng)時(shí)間。不過,Go 1.13 運(yùn)行時(shí)本身也改進(jìn)了內(nèi)存歸還給操作系統(tǒng)的策略,這在一定程度上平衡了這一點(diǎn)。

總的來說,Go 1.13 中 sync 包的改進(jìn)提升了常用同步原語的性能,并使 sync.Pool 在高并發(fā)和頻繁 GC 的場(chǎng)景下表現(xiàn)更加穩(wěn)定和高效。

責(zé)任編輯:姜華 來源: Piper蛋窩
相關(guān)推薦

2025-04-24 09:01:46

2025-04-22 08:02:23

2025-04-17 08:00:48

2025-04-18 08:07:12

2025-04-28 08:00:56

2025-04-14 08:06:04

2025-04-25 08:01:12

Go應(yīng)用程序部署

2025-04-15 08:00:53

2025-04-29 08:03:18

2025-05-06 08:00:35

2025-05-06 05:00:00

2025-05-06 00:00:08

2025-04-21 00:05:00

2025-04-21 08:00:56

2025-04-27 08:00:35

2025-04-27 00:00:01

Go 1.16Go 1.15接口

2025-04-21 00:00:00

Go 開發(fā)Go 語言Go 1.9

2025-04-30 09:02:46

2025-04-14 00:00:04

2025-04-11 08:02:38

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产一区影院 | 最新国产福利在线 | 日韩二区| 日本精品一区二区在线观看 | 精品免费视频 | av黄色在线播放 | 视频一区二区在线观看 | 久久专区 | 国产精品福利一区二区三区 | 97久久久 | 五月综合激情婷婷 | 国产精品久久久久久久久久 | 性一爱一乱一交一视频 | 久久久涩| 亚洲精品在线观看网站 | 日韩在线免费视频 | 国产精品美女久久久久久久久久久 | 午夜久久久 | 欧美极品视频在线观看 | 青春草在线| 国产精品一区二区av | 91久久久久久久久久久久久 | 北条麻妃国产九九九精品小说 | 亚洲在线一区二区三区 | 一区二区三区视频免费看 | 在线日韩欧美 | 国产精品亚洲欧美日韩一区在线 | 亚洲成人久久久 | 99久久精品视频免费 | 成人免费网站 | 国产精品a久久久久 | 国产99视频精品免视看9 | 亚洲欧美日韩一区二区 | 毛片免费视频 | 亚洲国产精品日韩av不卡在线 | www国产亚洲精品久久网站 | 天天操夜夜爽 | 亚洲精品自在在线观看 | 天天干天天插 | 97久久精品午夜一区二区 | 成人毛片视频在线播放 |