Go 1-21 中 sync-Once 的新擴展

在 Go 1.21 中, 增加了和 sync.Once 有關的三個函數。sync.Once 本身實現就非常簡單了,新增加的這三個函數到底是幹啥的?讓我們一起來看看。

sync.Once

我們常常使用 sync.Once 實現單例模式,它也非常的高效。

下面的代碼是官方的一個例子,運行它可以看到onceBody函數只會被執行一次:

package main

import (
 "fmt"
 "sync"
)

func main() {
 var once sync.Once
 onceBody := func() {
  fmt.Println("Only once")
 }
 done := make(chan bool)
 for i := 0; i < 10; i++ {
  go func() {
   once.Do(onceBody)
   done <- true
  }()
 }
 for i := 0; i < 10; i++ {
  <-done
 }
}

OnceFunc

func OnceFunc(f func()) func()

OnceFunc返回一個可以併發調用的函數,它可以被調用多次。即使返回的函數被調用多次,f也只會被調用一次。

下面的代碼onceBody只被執行了一次:

func main() {
 onceBody := func() {
  fmt.Println("Only once")
 }

 foo := sync.OnceFunc(onceBody)
 for i := 0; i < 10; i++ {
  foo()
 }
}

OnceValue

func OnceValue[T any](f func( "T any") T) func() T

OnceValue返回一個函數, 這個函數會返回 f 的返回值。多次調用都會返回同一個值。

下面的代碼中,randvalue只會被執行一次,返回結果記做n的話, 並且每次調用 bar 函數都會返回n。bar 可以併發的被調用。

 randvalue := func() int {
  return rand.Int()
 }
 bar := sync.OnceValue(randvalue)
 for i := 0; i < 10; i++ {
  fmt.Println(bar())
 }

同時,可以看到在標準庫中,泛型越來越被使用。

OnceValues

func OnceValues[T1, T2 any](f func( "T1, T2 any") (T1, T2)) func() (T1, T2)

OnceValuesOnceValue的功能類似,只不過返回兩個參數,僅此而已。

綜述一下:

稍微展開講一下 tuple。

如果想返回更多的返回值,自己模仿構造一個,或者把多個返回值封裝到一個對象中。

很多年前,我記得看一篇文章介紹某種編程語言的 tuple 是硬編碼的, 兩個元素的 tuple、三個元素的 tuple、四個元素的 tuple 等,忘記是哪個語言了。 所以你也可以模仿它。

go-tuple[1] 就是這樣實現的一個 go 語言的 tuple, 最多支持 9 個元素。

type T9
func FromArray9(arr [9]any) (T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9], error)
func FromArray9X(arr [9]any) T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]
func FromSlice9(values []any) (T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9], error)
func FromSlice9X(values []any) T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]
func New9(v1 Ty1, v2 Ty2, v3 Ty3, v4 Ty4, v5 Ty5, v6 Ty6, v7 Ty7, v8 Ty8, v9 Ty9) T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]
func (t T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) Array() [9]any
func (t T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) GoString() string
func (t T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) Len() int
func (t T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) Slice() []any
func (t T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) String() string
func (t T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) Values() (Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9)

順便說一下,上面我介紹的 Go 1.21 新增的三個內建函數之一的clear, 清除 map 的時候,只是把 map 置空,並不會 shrink map, 你可以看例子: https://go.dev/play/p/quVwNvAZAGJ?v=gotip 或者相關討論 https://github.com/golang/go/issues/56351

參考資料

[1]

go-tuple: https://github.com/barweiss/go-tuple

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/SFXe1bdBu0bLuHoGEFTz8g