go1.22的一些关键改动及代码演示

参考汇总文章

Go1.22 正式发布!包含语言变化、性能提高、标准库变动等重要特性

在电脑中安装多个版本的golang

由于我的电脑安装的是go的1.21版本,1.22版本改动很大,如果工作中部署的项目dockerFile中指定的镜像的go版本比1.22低的话,有一些语法会编译不通过,所以我在官方下载了.gz格式的go,然后解压到本地,再使用alias命令做了一个简单的别名,简单的演示一下go1.22的改动即可。

至于工作中是否有必要将go的版本升级到最新的1.22需要跟公司的同事一起商量。

golang所有镜像的地址

GoLang下载与安装-三种安装方式

下载后解压命令

tar -zxvf go1.22.1.darwin-amd64.tar.gz

设置别名

~~~完事后记得重启下终端 或者 source ~/.bash_profile

~~~

语言层面解决“惰性计算”问题

go1.21及之前版本遇到的并发情况下的“惰性计算“问题及解决方案

package main

import (
    "fmt"
    "testing"
)

// 有惰性计算问题
func TestT1(t *testing.T) {
    done := make(chan bool)

    values := []string{"a", "b", "c"}
    for _, v := range values {
        go func() {
            // Notice 使用go1.21编译 有惰性计算问题 打印:
            /*
                v:  c
                v:  c
                v:  c
            */
            fmt.Println("v: ", v)
            done <- true
        }()
    }

    // wait for all goroutines to complete before exiting
    for _ = range values {
        <-done
    }
}

// 解决方案1
func TestT2(t *testing.T) {
    done := make(chan bool)

    values := []string{"a", "b", "c"}

    for _, v := range values { // Notice 每次for循环 不会为v新申请内存

        // Notice 使用go1.21编译 有惰性计算问题 这样解决:
        // 在for循环中使用一个临时变量接收v,相当于重新申请一块内存
        currV := v

        go func() {
            /*
                v:  c
                v:  b
                v:  a
            */
            fmt.Println("v: ", currV)
            done <- true
        }()
    }

    // wait for all goroutines to complete before exiting
    for _ = range values {
        <-done
    }
}

// 解决方案2
func TestT3(t *testing.T) {
    done := make(chan bool)

    values := []string{"a", "b", "c"}

    for _, v := range values { // Notice 每次for循环 不会为v新申请内存

        // Notice 使用go1.21编译 有惰性计算问题 这样解决:
        // 将v当作参数传到子协程这个匿名函数中
        go func(currV string) {
            /*
                v:  c
                v:  a
                v:  b
            */
            fmt.Println("v: ", currV)
            done <- true
        }(v)
    }

    // wait for all goroutines to complete before exiting
    for _ = range values {
        <-done
    }
}

go1.22从语言层面解决了“惰性计算”问题

注意:下面的代码需要使用go1.22的编译器才能出来效果:

package main

import "fmt"

func main() {
    done := make(chan bool)

    values := []string{"a", "b", "c"}
    for _, v := range values { // Notice 每次for循环都会为v重新申请一块内存
        go func() {
            // Notice 使用go1.22编译 解决了惰性计算问题 打印:
            /*
                    v:  b    v的地址:  0xc000090030
                    v:  a    v的地址:  0xc000090020
                    v:  c    v的地址:  0xc000090040
            */
            fmt.Println("v: ", v, "   v的地址: ", &v)
            done <- true
        }()
    }

    // wait for all goroutines to complete before exiting
    for _ = range values {
        <-done
    }
}

go1.22的range关键字支持整型数据

package main

import "fmt"


func main() {
    // Notice 下面的代码只能在go1.22版本运行,以下的版本会报错
    for i := range 10 {
        fmt.Println(i)
    }
    fmt.Println("------------------------------")
    /*
            0
            1
            2
            3
            4
            5
            6
            7
            8
            9
    */
}

go1.22新的高效的math/rand/v2库

参考文章:Go1.22 新特性:新的 math/rand/v2 库,更快更标准!

新老版本的rand模块效率对比

package _rand_tests

import (
    "math/rand"
    randV2 "math/rand/v2"
    "testing"
)

const Max = 1e9

func BenchmarkRand(b *testing.B) {

    for i := 0; i < b.N; i++ {
        rand.Intn(Max)
    }
    /*
        goos: darwin
        goarch: amd64
        pkg: go122Project/_rand_tests
        cpu: VirtualApple @ 2.50GHz
        BenchmarkRand
        BenchmarkRand-10        70538617            17.08 ns/op
        PASS
    */
}

func BenchmarkRandV2(b *testing.B) {

    for i := 0; i < b.N; i++ {
        randV2.IntN(Max)
    }
    /*
        goos: darwin
        goarch: amd64
        pkg: go122Project/_rand_tests
        cpu: VirtualApple @ 2.50GHz
        BenchmarkRandV2
        BenchmarkRandV2-10        130344816             8.751 ns/op
        PASS
    */
}

删除Seed与Read方法

slices包新增Concat函数-切片拼接 

package _slices_tests

import (
    "fmt"
    "slices"
    "testing"
)

// go1.22之前的切片拼接
func TestS1(t *testing.T) {
    s1 := []string{"煎鱼", "咸鱼", "摸鱼"}
    s2 := []string{"炸鱼", "水鱼", "煎鱼"}
    s3 := []string{"whw", "naruto", "sasuke"}

    ret := append(s1, s2...)
    ret = append(ret, s3...)

    fmt.Println(ret) // [煎鱼 咸鱼 摸鱼 炸鱼 水鱼 煎鱼 whw naruto sasuke]
}

// go1.22新的切片拼接
// Notice 需要确保传入的切片类型都是一致的!!!
func TestS2(t *testing.T) {
    s1 := []string{"煎鱼", "咸鱼", "摸鱼"}
    s2 := []string{"炸鱼", "水鱼", "煎鱼"}
    s3 := []string{"whw", "naruto", "sasuke"}

    ret := slices.Concat(s1, s2, s3)

    fmt.Println("ret: ", ret) // [煎鱼 咸鱼 摸鱼 炸鱼 水鱼 煎鱼 whw naruto sasuke]

    a1 := []int{1, 3, 4}
    a2 := []int{22, 22, 22}

    fmt.Println(">> ", slices.Concat(a1, a2)) // [1 3 4 22 22 22]

}

slices其他的改动

Go1.22 新特性:Slices 变更 Concat、Delete、Insert 等函数,对开发挺有帮助!

~~~

clear函数清空slice的坑!

 

package main

import (
    "fmt"
)

type S1 struct {
    Name string
    Age  int
}

func main() {

    s1 := S1{Name: "s1", Age: 22}
    s2 := S1{Name: "s2", Age: 23}
    s3 := S1{Name: "s3", Age: 24}

    // slice
    slice1 := []*S1{&s1, &s2, &s3}
    fmt.Println("slice1: >>> ", len(slice1), slice1) // 3 [0x1400012c0a8 0x1400012c0c0 0x1400012c0d8]
    // _clear slice
    clear(slice1)
    // Notice clear切片后 注意!!!之前的元素会被 nil 替代!!!
    // Notice 对于切片 clear感觉还不如直接重新赋值一个新的空切片!!!
    fmt.Println("slice1_clear后: >>> ", len(slice1), slice1) // 3 [<nil> <nil> <nil>]

    // map 清空map不会有零值
    map1 := map[string]*S1{
        "s1": &s1,
        "s2": &s2,
        "s3": &s3,
    }
    fmt.Println("map1: >>> ", len(map1), map1) // 3 map[s1:0x1400012c0a8 s2:0x1400012c0c0 s3:0x1400012c0d8]
    // _clear map
    clear(map1)
    fmt.Println("map1_clear后: >>> ", len(map1), map1) // 0 map[]
    // Notice 测试下 clear后能不能再赋值
    map1["ss1"] = &s1
    fmt.Println("map1_clear后再赋值: >>> ", len(map1), map1) // 1 map[ss1:0x140000b40a8]
}

~~~

posted on 2024-02-18 15:23  江湖乄夜雨  阅读(611)  评论(0编辑  收藏  举报