在 Go 1.21 中, 增加了和 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
}
}
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()
}
}
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())
}
同时,可以看到在标准库中,泛型越来越被使用。
func OnceValues[T1, T2 any](f func( "T1, T2 any") (T1, T2)) func() (T1, T2)
OnceValues
和OnceValue
的功能类似,只不过返回两个参数,仅此而已。
综述一下:
稍微展开讲一下 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
go-tuple: https://github.com/barweiss/go-tuple