3

sync.Once的新扩展

 1 year ago
source link: https://colobu.com/2023/05/29/extends-sync-Once/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在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) 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)) func() (T1, T2)

OnceValuesOnceValue的功能类似,只不过返回两个参数,仅此而已。

综述一下:

  • 这三个函数返回的函数被调用的时候,分别返回0个、1个、2个返回值。
  • 返回的函数可以被并发的调用。
  • 如果f被执行的时候panic, 返回的函数被调用的时候也会panic,panic的值和f的panic一样。

稍微展开讲一下tuple。

如果想返回更多的返回值,自己模仿构造一个,或者把多个返回值封装到一个对象中。

很多年前,我记得看一篇文章介绍某种编程语言的tuple是硬编码的, 两个元素的tuple、三个元素的tuple、四个元素的tuple等,忘记是哪个语言了。
所以你也可以模仿它。

go-tuple就是这样实现的一个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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK