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

Go 1.24 相比 Go 1.23 有哪些值得注意的改動?

開發(fā) 前端
官方發(fā)布說明:https://go.dev/doc/go1.24Go 1.24 值得關(guān)注的改動:泛型類型別名?: Go 1.24 完全支持泛型類型別名(generic type aliases),允許類型別名像定義類型一樣進行參數(shù)化。工具鏈升級?:?go.mod?文件新增?tool?指令用于追蹤可執(zhí)行依賴;新增?GOAUTH?環(huán)境變量用于私有模塊認證;go build?默認將版本控制信息嵌入二進制

官方發(fā)布說明:https://go.dev/doc/go1.24

Go 1.24 值得關(guān)注的改動:

  1. 泛型類型別名 : Go 1.24 完全支持泛型類型別名(generic type aliases),允許類型別名像定義類型一樣進行參數(shù)化。
  2. 工具鏈升級 : go.mod 文件新增 tool 指令用于追蹤可執(zhí)行依賴;新增 GOAUTH 環(huán)境變量用于私有模塊認證;go build 默認將版本控制信息嵌入二進制文件。
  3. 運行時性能提升 : 通過基于 Swiss Tables 的新 map 實現(xiàn)、更高效的小對象內(nèi)存分配和新的內(nèi)部互斥鎖實現(xiàn),平均 CPU 開銷降低 2-3%。
  4. 限制目錄的文件系統(tǒng)訪問 : 新增 os.Root 類型,提供在特定目錄內(nèi)執(zhí)行文件系統(tǒng)操作的能力,防止訪問目錄外的路徑。
  5. 新的基準測試函數(shù) : 新增 testing.B.Loop 方法,用于替代傳統(tǒng)的 b.N 循環(huán),執(zhí)行基準測試迭代更快速且不易出錯。
  6. 改進的 Finalizer : 新增 runtime.AddCleanup 函數(shù),提供比 runtime.SetFinalizer 更靈活、高效且不易出錯的對象清理機制。
  7. 新增 weak 包 : 提供弱指針(weak pointers),用于構(gòu)建內(nèi)存高效的數(shù)據(jù)結(jié)構(gòu),如弱引用映射、規(guī)范化映射和緩存。

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

泛型類型別名支持

Go 1.24 現(xiàn)在完全支持泛型類型別名(generic type aliases)。這意味著類型別名可以像定義的類型(defined types)一樣,擁有自己的類型參數(shù)列表。在此之前,類型別名無法直接參數(shù)化。

這項改動使得代碼組織更加靈活。例如,你可以為一個已有的泛型類型創(chuàng)建一個別名,而無需重復(fù)其類型參數(shù)約束:

package main

import "fmt"

// 一個泛型類型
type Vector[T any] []T

// Go 1.24 起,可以為泛型類型創(chuàng)建別名
// VectorAlias 和 Vector[T] 是同一類型
type VectorAlias[T any] = Vector[T]

func main() {
    var v VectorAlias[int] = []int{1, 2, 3}
    v = append(v, 4)
    fmt.Println(v) // 輸出: [1 2 3 4]

    var originalV Vector[int] = v // 可以直接賦值,因為它們是同一類型
    fmt.Println(originalV)      // 輸出: [1 2 3 4]
}

類型別名也可以有自己的約束,只要它們與原始類型兼容:

package main

import (
    "fmt"
    "golang.org/x/exp/constraints"
)

// 定義一個帶約束的泛型接口
type Number interface {
    constraints.Integer | constraints.Float
}

// 定義一個泛型結(jié)構(gòu)體
type Point[T Number] struct {
    X, Y T
}

// 為 Point 創(chuàng)建一個泛型類型別名,使用相同的約束
type PointAlias[T Number] = Point[T]

// 也可以創(chuàng)建更具體約束的別名 (如果原始類型允許)
// 例如,如果我們只想為整數(shù)創(chuàng)建別名
type IntPointAlias[T constraints.Integer] = Point[T]

func main() {
    var p1 PointAlias[float64] = Point[float64]{X: 1.5, Y: 2.5}
    fmt.Println("PointAlias[float64]:", p1) // PointAlias[float64]: {1.5 2.5}

    var p2 IntPointAlias[int] = Point[int]{X: 10, Y: 20}
    fmt.Println("IntPointAlias[int]:", p2) // IntPointAlias[int]: {10 20}

    // 下面的代碼會編譯錯誤,因為 string 不滿足 Number 約束
    // var p3 PointAlias[string] = Point[string]{X: "a", Y: "b"}
}

這個特性可以通過設(shè)置 GOEXPERIMENT=noaliastypeparams 來禁用,但這個選項計劃在 Go 1.25 中移除。

Go 命令和工具鏈的增強

Go 1.24 對 Go 命令和工具鏈進行了一些重要的改進,旨在提升開發(fā)體驗和構(gòu)建過程的可靠性。

1. 使用 tool 指令管理工具依賴

以前,開發(fā)者通常在項目根目錄下創(chuàng)建一個 tools.go 文件,并使用空導(dǎo)入(blank imports)來記錄項目所需的構(gòu)建工具(如代碼生成器、linter 等),以便 go mod tidy 不會將其移除。

Go 1.24 引入了 tool 指令,可以直接在 go.mod 文件中聲明這些工具依賴。

// go.mod
module example.com/mymodule
go 1.24

toolchain go1.24.0 // Go 1.21 引入,指定期望的工具鏈版本

require (
    // ... 其他依賴 ...
)

// 新增的 tool 指令塊
tool (
    golang.org/x/tools/cmd/stringer v0.19.0
    honnef.co/go/tools/cmd/staticcheck latest // 也可以使用 latest
)

你可以使用 go get -tool <package> 命令來添加或更新工具依賴,例如:

go get -tool honnef.co/go/tools/cmd/staticcheck

go mod tidy 現(xiàn)在也會考慮 tool 依賴。

2. 增強的 go tool 命令

go tool 命令現(xiàn)在不僅可以運行 Go 發(fā)行版自帶的工具(如 go tool pprof, go tool vet),還可以直接運行在 go.mod 中通過 tool 指令聲明的工具。

go tool staticcheck ./...
go tool stringer -type=MyType

3. 新的 tool 元模式

可以使用 tool 作為元模式(meta-pattern)來指代 go.mod 中聲明的所有工具。

# 更新所有工具依賴到最新版本
go get tool

# 將所有工具安裝到 $GOBIN 目錄
go install tool

4. 可執(zhí)行文件緩存

go run 和 go tool(用于運行 tool 指令聲明的工具時)構(gòu)建的可執(zhí)行文件現(xiàn)在會被緩存到 Go 的構(gòu)建緩存中。這使得重復(fù)運行這些命令更快,但會增加構(gòu)建緩存的大小。

5. JSON 構(gòu)建輸出

go build 和 go install 命令新增了 -json 標志,可以將構(gòu)建過程的輸出和錯誤信息以結(jié)構(gòu)化的 JSON 格式輸出到標準輸出。這對于自動化構(gòu)建和集成分析非常有用。

go test -json 現(xiàn)在也會在輸出測試結(jié)果的同時,以 JSON 格式穿插報告構(gòu)建過程的輸出和錯誤。如果這給現(xiàn)有的測試集成系統(tǒng)帶來問題,可以通過設(shè)置 GODEBUG=gotestjsonbuildtext=1 恢復(fù)到舊的行為(構(gòu)建輸出為文本)。

6. GOAUTH 環(huán)境變量

新增 GOAUTH 環(huán)境變量,為獲取私有模塊提供了更靈活的認證方式。你可以配置不同的 URL 前綴使用不同的認證憑據(jù)。詳情請查閱 go help goauth。

7. 構(gòu)建時嵌入版本控制信息

go build 現(xiàn)在默認會根據(jù)版本控制系統(tǒng)(VCS)的信息(如 Git 的標簽和提交哈希)將主模塊的版本信息嵌入到編譯后的二進制文件中。如果工作目錄存在未提交的更改,版本信息會附加 +dirty 后綴。

你可以通過 runtime/debug.ReadBuildInfo() 讀取這些信息。如果不想嵌入這些信息,可以使用 -buildvcs=false 標志。

8. 工具鏈追蹤

可以通過設(shè)置 GODEBUG=toolchaintrace=1 來追蹤 go 命令在選擇和執(zhí)行工具鏈(編譯器、鏈接器等)時的詳細過程,這有助于調(diào)試工具鏈相關(guān)的問題。

限制目錄的文件系統(tǒng)訪問 (os.Root)

Go 1.24 在 os 包中引入了一個重要的新特性:os.Root 類型,用于將文件系統(tǒng)操作限制在指定的目錄樹內(nèi)。

os.OpenRoot(dir string) 函數(shù)會打開一個目錄 dir 并返回一個 os.Root 對象。之后,所有通過這個 os.Root 對象進行的文件系統(tǒng)操作(如 Open, Create, Mkdir, Stat, ReadDir 等)都將被限制在 dir 目錄及其子目錄下。任何試圖訪問 dir 目錄之外路徑的操作,包括通過 .. 或解析符號鏈接(symbolic links)到目錄外的情況,都會失敗并返回錯誤。

這對于需要處理不可信路徑輸入或需要在沙盒環(huán)境中操作文件的應(yīng)用程序(例如 Web 服務(wù)器、插件系統(tǒng))來說,是一個非常有用的安全增強功能。

下面是一個簡單的例子:

package main

import (
    "fmt"
    "log"
    "os"
    "path/filepath"
)

func main() {
    // 創(chuàng)建一個臨時根目錄
    rootDir, err := os.MkdirTemp("", "osroot-demo-root")
    if err != nil {
        log.Fatalf("創(chuàng)建根目錄失敗: %v", err)
    }
    defer os.RemoveAll(rootDir) // 清理

    // 在根目錄下創(chuàng)建一些內(nèi)容
    safeFilePath := filepath.Join(rootDir, "safe_file.txt")
    err = os.WriteFile(safeFilePath, []byte("安全內(nèi)容"), 0644)
    if err != nil {
        log.Fatalf("寫入安全文件失敗: %v", err)
    }
    subDir := filepath.Join(rootDir, "subdir")
    err = os.Mkdir(subDir, 0755)
    if err != nil {
        log.Fatalf("創(chuàng)建子目錄失敗: %v", err)
    }
    fmt.Printf("測試根目錄: %s\n", rootDir)
    fmt.Printf("安全文件路徑: %s\n", safeFilePath)

    // 打開根目錄,獲取 os.Root
    root, err := os.OpenRoot(rootDir)
    if err != nil {
        log.Fatalf("os.OpenRoot 失敗: %v", err)
    }
    // 注意:目前的實現(xiàn) os.Root 也需要 Close,未來版本可能改變
    // defer root.Close()

    // 1. 嘗試在 root 內(nèi)打開文件 (成功)
    f, err := root.Open("safe_file.txt") // 使用相對于 rootDir 的路徑
    if err != nil {
        log.Printf("在 root 內(nèi)打開 safe_file.txt 失敗: %v", err)
    } else {
        fmt.Println("成功在 root 內(nèi)打開 safe_file.txt")
        f.Close()
    }

    // 2. 嘗試在 root 內(nèi)創(chuàng)建目錄 (成功)
    err = root.Mkdir("another_dir", 0755)
    if err != nil {
        log.Printf("在 root 內(nèi)創(chuàng)建 another_dir 失敗: %v", err)
    } else {
        fmt.Println("成功在 root 內(nèi)創(chuàng)建 another_dir")
    }

    // 3. 嘗試使用 ".." 訪問 root 之外 (失敗)
    _, err = root.Open("../outside_file.txt")
    if err != nil {
        fmt.Printf("正確地失敗了: 嘗試使用 .. 訪問外部 (%v)\n", err) // 預(yù)計錯誤
    } else {
        log.Fatalf("錯誤:竟然成功訪問了外部目錄!")
    }

    // 4. 嘗試使用絕對路徑訪問 root 之內(nèi) (失敗,os.Root 的方法只接受相對路徑)
    _, err = root.Open(safeFilePath)
    if err != nil {
        fmt.Printf("正確地失敗了: 嘗試使用絕對路徑 %s (%v)\n", safeFilePath, err) // 預(yù)計錯誤
    } else {
        log.Fatalf("錯誤:竟然成功使用絕對路徑訪問!")
    }
}
測試根目錄: /tmp/osroot-demo-root1840017364
安全文件路徑: /tmp/osroot-demo-root1840017364/safe_file.txt
成功在 root 內(nèi)打開 safe_file.txt
成功在 root 內(nèi)創(chuàng)建 another_dir
正確地失敗了: 嘗試使用 .. 訪問外部 (openat ../outside_file.txt: path escapes from parent)
正確地失敗了: 嘗試使用絕對路徑 /tmp/osroot-demo-root1840017364/safe_file.txt (openat /tmp/osroot-demo-root1840017364/safe_file.txt: path escapes from parent)

新的基準測試函數(shù) (testing.B.Loop)

Go 1.24 在 testing 包中引入了一個新的方法 (*testing.B).Loop,用于編寫基準測試(benchmarks)。它旨在替代傳統(tǒng)的 for i := 0; i < b.N; i++ 循環(huán),提供更精確、更不易出錯的基準測試方式。

傳統(tǒng)的 b.N 循環(huán)存在兩個主要問題:

  1. 如果測試函數(shù)包含昂貴的設(shè)置(setup)或清理(cleanup)代碼,這些代碼可能會在 b.N 的每次迭代中都執(zhí)行(或者至少部分執(zhí)行),從而干擾測試結(jié)果。
  2. 編譯器有時會過度優(yōu)化循環(huán)體,甚至完全消除它,特別是當(dāng)循環(huán)結(jié)果未被使用時,導(dǎo)致測試結(jié)果失真。

b.Loop() 方法解決了這些問題:

  1. 設(shè)置/清理只執(zhí)行一次 :包含 b.Loop() 的基準測試函數(shù)本身,對于每次 -count 運行(默認 -count=1),只會完整執(zhí)行一次。b.Loop() 內(nèi)部會根據(jù)需要自動調(diào)整迭代次數(shù)來達到穩(wěn)定的測量結(jié)果,但外層的設(shè)置和清理代碼只會執(zhí)行一次。
  2. 防止過度優(yōu)化 :b.Loop() 的實現(xiàn)機制有助于保持函數(shù)調(diào)用的參數(shù)和結(jié)果“存活”(live),防止編譯器將核心測試邏輯完全優(yōu)化掉。

使用 b.Loop() 的基本模式如下:

package main_test

import (
    "strconv"
    "testing"
)

// 待測試的函數(shù)
func formatInt(i int) string {
    return strconv.Itoa(i)
}

// 使用 b.Loop() 的基準測試
func BenchmarkFormatIntLoop(b *testing.B) {
    // 1. 在循環(huán)外執(zhí)行設(shè)置代碼
    num := 12345
    var result string // 聲明一個變量來接收結(jié)果,防止優(yōu)化

    b.ReportAllocs() // 可選:報告內(nèi)存分配
    b.ResetTimer()   // 重置計時器,忽略設(shè)置時間

    // 2. 使用 b.Loop() 替代 for i := 0; i < b.N; i++
    for b.Loop() {
        // 3. 將要測試的核心操作放在循環(huán)體內(nèi)
        result = formatInt(num)
    }

    // 4. (可選)使用結(jié)果,進一步防止優(yōu)化
    _ = result
}

// 傳統(tǒng)方式對比
func BenchmarkFormatIntOld(b *testing.B) {
    num := 12345
    var result string
    b.ReportAllocs()
    b.ResetTimer() // ResetTimer 在循環(huán)外
    for i := 0; i < b.N; i++ { // 傳統(tǒng)的 b.N 循環(huán)
        result = formatInt(num)
    }
    _ = result
}

在 BenchmarkFormatIntLoop 中,num 的初始化和 result 的聲明只在每次 -count 運行時執(zhí)行一次。b.Loop() 會負責(zé)執(zhí)行核心操作 formatInt(num) 足夠的次數(shù)以獲取可靠的性能數(shù)據(jù)。

改進的 Finalizer (runtime.AddCleanup)

Go 長期以來提供了 runtime.SetFinalizer 函數(shù),允許開發(fā)者為一個對象設(shè)置一個“終結(jié)器”(finalizer)函數(shù)。當(dāng)垃圾回收器(GC)確定該對象不再可達時,終結(jié)器函數(shù)會被調(diào)用,通常用于釋放對象關(guān)聯(lián)的非內(nèi)存資源(如文件句柄、數(shù)據(jù)庫連接等)。

然而,runtime.SetFinalizer 有一些眾所周知的缺點:

  • 一個對象只能設(shè)置一個終結(jié)器。
  • 不能為指向?qū)ο髢?nèi)部(例如結(jié)構(gòu)體字段的地址)的指針設(shè)置終結(jié)器。
  • 如果對象參與了循環(huán)引用(cycle),即使對象實際上已經(jīng)不再使用,終結(jié)器也可能永遠不會執(zhí)行,導(dǎo)致資源泄漏。
  • 終結(jié)器會延遲對象本身及其引用的其他對象的內(nèi)存回收。

Go 1.24 引入了 runtime.AddCleanup 函數(shù),提供了一個更靈活、更高效、更不易出錯的替代方案。

runtime.AddCleanup 的主要優(yōu)點:

  • 多個清理函數(shù) :可以為一個對象關(guān)聯(lián)多個清理函數(shù)。它們會在對象不可達后(不保證順序)被調(diào)用。
  • 支持內(nèi)部指針 :可以為指向?qū)ο髢?nèi)部的指針(interior pointers)添加清理函數(shù)。
  • 循環(huán)引用更安全 :通常情況下,即使對象存在于循環(huán)引用中,只要該循環(huán)整體不再可達,關(guān)聯(lián)的清理函數(shù)也能被執(zhí)行。
  • 不延遲內(nèi)存回收 :清理函數(shù)的執(zhí)行通常不會延遲對象本身或其引用對象的內(nèi)存釋放。

Go 團隊建議新代碼優(yōu)先使用 runtime.AddCleanup 而不是 runtime.SetFinalizer。

使用示例:

package main

import (
 "fmt"
 "runtime"
 "time"
)

type FileHandle struct {
 fd   int
 name string
}

// 定義清理函數(shù)所需的參數(shù)類型
type cleanupData struct {
 fd   int
 name string
}

func openFile(name string, fd int) *FileHandle {
 handle := &FileHandle{fd: fd, name: name}
 fmt.Printf("打開文件 '%s' (fd=%d)\n", name, fd)

 // 準備清理數(shù)據(jù)
 data := cleanupData{fd: handle.fd, name: handle.name}

 // 注冊第一個清理函數(shù)
 runtime.AddCleanup(handle, func(d cleanupData) {
  fmt.Printf("清理函數(shù): 關(guān)閉文件 '%s' (fd=%d)\n", d.name, d.fd)
  // 實際關(guān)閉文件操作,例如 close(d.fd)
 }, data)

 // 注冊第二個清理函數(shù)
 runtime.AddCleanup(handle, func(d cleanupData) {
  fmt.Printf("清理函數(shù)2: 文件 '%s' 已處理完畢\n", d.name)
 }, data)

 return handle
}

func main() {
 func() {
  f1 := openFile("config.txt", 1)
  f2 := openFile("data.log", 2)
  _ = f1 // 使用 f1, f2
  _ = f2
  fmt.Println("內(nèi)部作用域即將結(jié)束...")
 }()

 fmt.Println("強制執(zhí)行 GC...")
 runtime.GC() // 觸發(fā) GC

 // 給清理函數(shù)執(zhí)行時間
 time.Sleep(100 * time.Millisecond)
 runtime.GC() // 可能需要再次 GC
 time.Sleep(100 * time.Millisecond)

 fmt.Println("程序結(jié)束")
}
打開文件 'config.txt' (fd=1)
打開文件 'data.log' (fd=2)
內(nèi)部作用域即將結(jié)束...
強制執(zhí)行 GC...
清理函數(shù): 關(guān)閉文件 'data.log' (fd=2)
清理函數(shù)2: 文件 'data.log' 已處理完畢
清理函數(shù): 關(guān)閉文件 'config.txt' (fd=1)
清理函數(shù)2: 文件 'config.txt' 已處理完畢
程序結(jié)束

注意 :清理函數(shù)的執(zhí)行時機依賴于 GC。它們會在對象不可達后的某個時間點執(zhí)行,但不保證立即執(zhí)行,也不保證在程序退出前一定執(zhí)行。因此,對于必須在程序退出前完成的關(guān)鍵清理操作(如刷新緩沖區(qū)),仍需依賴 defer 或其他顯式機制。

新增 weak 包提供弱指針

Go 1.24 引入了一個新的標準庫包 weak,提供了對弱指針(weak pointers)的支持。

弱指針是一種特殊的指針,它指向一個對象,但 不會 阻止該對象被垃圾回收器(GC)回收。如果對象只被弱指針引用,那么在下一次 GC 循環(huán)中,該對象就可能被回收。

weak 包主要提供了 weak.Pointer[T] 類型:

  • weak.Make[T](p *T) weak.Pointer[T]: 從一個普通的強指針 p 創(chuàng)建一個弱指針。
  • wp.Strong() *T: 嘗試從弱指針 wp 獲取一個指向原始對象的強指針。如果對象還未被 GC 回收,則返回該強指針;如果對象已經(jīng)被回收,則返回 nil。通過 Strong() 獲取到的強指針會阻止對象被回收,直到該強指針不再被使用。

弱指針是一個相對低級的原語,主要用于構(gòu)建內(nèi)存敏感或需要特殊生命周期管理的數(shù)據(jù)結(jié)構(gòu),例如:

  • 弱引用映射(Weak Maps) : Key 或 Value 是弱引用的映射。當(dāng) Key 或 Value 被 GC 回收后,相應(yīng)的條目可以從映射中自動移除,避免內(nèi)存泄漏。常用于將元數(shù)據(jù)關(guān)聯(lián)到對象上,而又不影響對象的生命周期。
  • 規(guī)范化映射(Canonicalization Maps) : 確保某個值(例如,一個大的不可變對象)在內(nèi)存中只有一個實例。弱指針可以用于檢查現(xiàn)有實例是否已被回收。
  • 緩存(Caches) : 實現(xiàn)當(dāng)緩存項不再被外部強引用時可以自動從緩存中移除的策略,從而更有效地利用內(nèi)存。

weak 包通常需要與 runtime.AddCleanup(當(dāng)對象被回收時執(zhí)行清理邏輯,例如從映射中移除弱指針)或 maphash.Comparable(使指針可以用作 map 的 key)結(jié)合使用。

由于弱指針的復(fù)雜性和潛在的微妙行為,直接使用它需要非常謹慎。大多數(shù)應(yīng)用程序開發(fā)者可能不需要直接使用 weak 包,但它為庫開發(fā)者提供了構(gòu)建更高級、內(nèi)存更高效的抽象提供了基礎(chǔ)。

下面是一個非常簡化的使用弱指針作為緩存值的例子( 注意:這是一個高度簡化的示例,并非生產(chǎn)級的弱緩存實現(xiàn) ):

package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
    "weak" // 導(dǎo)入 weak 包
)

type CachedData struct {
    ID   int
    Data string
}

var cache = struct {
    sync.Mutex
    // 使用 string 作為 key,弱指針指向 *CachedData 作為 value
    m map[string]weak.Pointer[CachedData]
}{
    m: make(map[string]weak.Pointer[CachedData]),
}

func getData(id string) *CachedData {
    cache.Lock()
    wp, ok := cache.m[id]
    cache.Unlock() // 盡快解鎖

    if ok {
        // 嘗試從弱指針獲取強指針
        strongPtr := wp.Strong()
        if strongPtr != nil {
            fmt.Printf("緩存命中: %s\n", id)
            return strongPtr // 對象仍然存活,返回強指針
        }
        // 對象已被 GC,但可能 finalizer/cleanup 還沒清理 map
        fmt.Printf("緩存失效 (GC'd): %s\n", id)
        // 可以在這里主動清理 map 條目
        // cache.Lock()
        // delete(cache.m, id)
        // cache.Unlock()
    }

    fmt.Printf("緩存未命中或失效,重新加載: %s\n", id)
    // 模擬從數(shù)據(jù)庫或其他來源加載數(shù)據(jù)
    newData := &CachedData{ID: len(cache.m), Data: fmt.Sprintf("Data for %s", id)}

    // 創(chuàng)建弱指針并存入緩存
    wp = weak.Make(newData)
    cache.Lock()
    cache.m[id] = wp
    cache.Unlock()

    // 重要:添加清理函數(shù),當(dāng) newData 被 GC 時,從緩存中移除弱指針
    // 否則弱指針對象本身會留在 map 中造成泄漏
    runtime.AddCleanup(newData, func(id string) {
        fmt.Printf("清理函數(shù): 移除緩存條目 %s (關(guān)聯(lián)對象已 GC)\n", id)
        cache.Lock()
        // 檢查當(dāng)前的弱指針是否還是當(dāng)初設(shè)置的那個,以及它是否確實已死
        if currentWp, exists := cache.m[id]; exists && currentWp.Strong() == nil {
            delete(cache.m, id)
        }
        cache.Unlock()
    }, id)

    return newData
}

func main() {
    d1 := getData("item1") // 加載并緩存 item1
    fmt.Printf("獲取到 d1: %+v\n", *d1)

    d2 := getData("item2") // 加載并緩存 item2
    fmt.Printf("獲取到 d2: %+v\n", *d2)

    // 再次獲取 item1,應(yīng)該命中緩存
    d1_again := getData("item1")
    fmt.Printf("再次獲取到 d1: %+v\n", *d1_again)

    // 移除對 d1 和 d1_again 的強引用
    d1 = nil
    d1_again = nil
    fmt.Println("移除了對 item1 數(shù)據(jù)的強引用")

    // 強制 GC
    fmt.Println("執(zhí)行 GC...")
    runtime.GC()
    time.Sleep(100 * time.Millisecond) // 等待清理函數(shù)執(zhí)行
    runtime.GC() // 可能需要多次 GC
    time.Sleep(100 * time.Millisecond)

    // 嘗試再次獲取 item1,預(yù)期緩存失效,重新加載
    d1_final := getData("item1")
    fmt.Printf("最終獲取到 d1: %+v\n", *d1_final)

    // 獲取 item2,應(yīng)該仍然在緩存中
    d2_again := getData("item2")
    fmt.Printf("再次獲取到 d2: %+v\n", *d2_again)
    _ = d2_again // 使用d2_again
}

這個例子展示了弱指針的基本用法:通過 weak.Make 創(chuàng)建,通過 Strong 獲取強引用,并結(jié)合 runtime.AddCleanup 在對象被回收后清理相關(guān)聯(lián)的弱指針記錄。

筆者使用 go 1.24.0 ,但是上述例子報錯:

$ go version
go version go1.24.0 linux/amd64

$ go run main.go
# command-line-arguments
./main.go:31:25: wp.Strong undefined (type weak.Pointer[CachedData] has no field or method Strong)
./main.go:60:66: currentWp.Strong undefined (type weak.Pointer[CachedData] has no field or method Strong)

這與官方文檔以及源碼相悖:

// src/internal/weak/pointer.go

// Strong creates a strong pointer from the weak pointer.
// Returns nil if the original value for the weak pointer was reclaimed by
// the garbage collector.
// If a weak pointer points to an object with a finalizer, then Strong will
// return nil as soon as the object's finalizer is queued for execution.
func (p Pointer[T]) Strong() *T {
 return (*T)(runtime_makeStrongFromWeak(p.u))
}


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

2025-05-06 05:00:00

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 00:00:08

2025-04-21 00:05:00

2025-04-23 08:02:40

2025-04-21 08:00:56

2025-04-24 09:01:46

2025-04-27 08:00:35

2025-04-21 00:00:00

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

2025-04-22 08:02:23

2025-04-30 09:02:46

2025-04-14 00:00:04

2025-04-27 00:00:01

Go 1.16Go 1.15接口

2025-04-11 08:02:38

點贊
收藏

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

主站蜘蛛池模板: 亚洲视频免费播放 | 久久久久久久国产 | 福利在线观看 | 99久久婷婷国产综合精品电影 | 日韩超碰 | 亚洲精品视频免费 | 在线小视频 | 国产激情视频在线观看 | 青青草国产在线观看 | 91视频日本 | 国产一区不卡 | 青娱乐一区二区 | 成人免费共享视频 | 亚洲一区二区久久 | 北条麻妃99精品青青久久主播 | 午夜影院污 | 久久久久免费精品国产小说色大师 | 欧美一区二区视频 | 四虎影院久久 | 综合久| av激情在线| 成人在线免费看 | 亚洲欧洲日韩 | 亚洲欧美日韩在线 | 国产黄色小视频在线观看 | 香蕉久久a毛片 | 日韩看片 | 91精品国产综合久久福利软件 | 国产成人免费视频 | 91在线看| 欧美成人精品激情在线观看 | 欧美激情综合色综合啪啪五月 | 久久精品综合 | av毛片| 欧美在线天堂 | 一区二区三区四区毛片 | av网站在线播放 | 蜜桃视频在线观看免费视频网站www | 亚洲毛片一区二区 | 欧美日韩中文字幕 | 81精品国产乱码久久久久久 |