go特性总结(1.13~1.16)


当前第2页 返回上一页

1.6.go test -v 现在将 t.Log 输出流式传输,而不是在所有测试数据结束时输出

2.goroutine 支持异步抢占

在Go1.1版本中,调度器还不支持抢占式调度,只能依靠 goroutine 主动让出 CPU 资源,存在非常严重的调度问题。

Go1.12中编译器在特定时机插入函数,通过函数调用作为入口触发抢占,实现了协作式的抢占式调度。但是这种需要函数调用主动配合的调度方式存在一些边缘情况,就比如说下面的例子:

func main() {

        runtime.GOMAXPROCS(1) 

        go func() {

                for {

                }

        }()

        time.Sleep(time.Millisecond)

        println("OK")

}

上面代码中,其中创建一个goroutine并挂起, main goroutine 优先调用了 休眠,此时唯一的 P 会转去执行 for 循环所创建的 goroutine,进而 main goroutine 永远不会再被调度。换一句话说在Go1.14之前,上边的代码永远不会输出OK,因为这种协作式的抢占式调度是不会使一个没有主动放弃执行权、且不参与任何函数调用的goroutine被抢占。

Go1.14 实现了基于信号的真抢占式调度解决了上述问题。Go1.14 程序启动时, 会在函数runtime.sighandler 中注册了 SIGURG 信号的处理函数 runtime.doSigPreempt,在触发垃圾回收的栈扫描时,调用函数挂起goroutine,并向M发送信号,M收到信号后,会让当前goroutine陷入休眠继续执行其他的goroutine。

3.testing 包新增CleanUp 方法

testing包的T、B和TB都加上了CleanUp方法,它将以后进先出的方式执行 f(如果注册多个的话)。

如下代码,输出结果是 test cleanup, clear resource:

func TestCleanup(t *testing.T) {

  t.Cleanup(func() {

      t.Log("clear resource")

  })

  t.Log("test cleanup")

}

#### 4.允许嵌入具有重叠方法集的接口

下面接口定义在 Go1.14 之前是不允许的

type ReadWriteCloser interface {

    io.ReadCloser

    io.WriteCloser

}

因为 io.ReadCloser 和 io.WriteCloser 中 Close 方法重复了,编译时会提示:duplicate method Close。在Go1.14中支持了这种重复接口集

go1.15

1. 1.15版本变化

1.1.对Go链接器的实质性改进

1.2.改进了对高核心计数的小对象的分配

1.3.X.509 CommonName弃用

1.4.GOPROXY支持跳过返回错误的代理

1.5.新增了一个time/tzdata包

1.6.核心库的一些改进

2. 核心库的一些改进

2.1 time.Ticker增加了一个Reset方法支持改变ticker的duration。 time.Ticker是一个周期性的定时器,内置一个周期性传递时间的Channel。 使用time.NewTicker(d Duration)函数创建一个Ticker,这个Ticker内置一个通道字段,每个时间间隔会向这个通道发送当前的时间。ticker会调整时间间隔或者丢弃消息以适应反应慢的接收者。

func TestTickerReset(t *testing.T) {

wait := make(chan struct{})

ticker := time.NewTicker(time.Second * 1)

go func() {

defer close(wait)

for i := 0; i < 5; i++ {

t.Log(<-ticker.C)

if i == 2 {

ticker.Reset(time.Second * 2)

}

}

}()

<-wait

}

2.2 time/tzdata是Go 1.15新增加的包,当系统找不到时区数据时,通过导入这个包,可以在程序中内嵌时区数据。 导入这个包会使程序大小增加大约800KB,注意time/tzdata这个包应该是在程序的main包中导入的,而不要在一个libary项目中导入和使用。 另外也可以通过编译时传递-tags timetzdata来实现同样的效果。

3.panic展现形式变化

在Go 1.15之前,如果传给panic的值是bool, complex64, complex128, float32, float64, int, int8, int16, int32, int64, string, uint, uint8, uint16, uint32, uint64, uintptr等原生类型的值,那么panic在触发时会输出具体的值,比如:

package main

func foo() {

    var i uint32 = 17

    panic(i)

}

func main() {

    foo()

}

1.15以上版本:

panic: main.myint(27)

goroutine 1 [running]:

main.bar(...)

        /Users/chengaosheng/go/test/test.go:63

main.main()

        /Users/chengaosheng/go/test/test.go:56 +0x39

exit status 2

1.15以下版本:

panic: (main.myint) (0x105fca0,0xc00008e000)

goroutine 1 [running]:

main.bar(...)

    /Users/tonybai/go/src/github.com/bigwhite/experiments/go1.15-examples/runtime/panic.go:12

main.main()

    /Users/tonybai/go/src/github.com/bigwhite/experiments/go1.15-examples/runtime/panic.go:17 +0x39

exit status 2

Go 1.15针对此情况作了展示优化,即便是派生于这些原生类型的自定义类型变量,panic也可以输出其值。

4. 标准库

4.1 增加json解码限制

json包是日常使用最多的go标准库包之一,在Go 1.15中,go按照json规范的要求,为json的解码增加了一层限制。如果一旦传入的json文本数据缩进深度超过maxNestingDepth,那json包就会panic。当然,绝大多数情况下,我们是碰不到缩进10000层的超大json文本的。因此,该limit对于99.9999%的gopher都没啥影响。

4.2 reflect包

Go 1.15版本之前reflect包

例子:

package main

import "reflect"

type u struct{}

func (u) M() { println("M") }

type t struct {

    u

    u2 u

}

func call(v reflect.Value) {

    defer func() {

        if err := recover(); err != nil {

            println(err.(string))

        }

    }()

    v.Method(0).Call(nil)

}

func main() {

    v := reflect.ValueOf(t{}) // v := t{}

    call(v)                  // v.M()

    call(v.Field(0))          // v.u.M()

    call(v.Field(1))          // v.u2.M()

}

1.15以上版本:

172-15-70-45:test chengaosheng$ go run test.go

M

reflect: reflect.Value.Call using value obtained using unexported field

reflect: reflect.Value.Call using value obtained using unexported field

我们看到reflect无法调用非导出字段u和u2的导出方法了。但是reflect依然可以通过提升到类型t的方法来间接使用u的导出方法,正如运行结果中的第一行输出。

**这一改动可能会影响到遗留代码中使用reflect调用以类型嵌入形式存在的非导出字段方法的代码**,如果你的代码中存在这样的问题,可以直接通过提升(promote)到包裹类型(如例子中的t)中的方法(如例子中的call(v))来替代之前的方式。

 go1.16

 1.Go 语言所打包的二进制文件中会包含配置文件的联同编译和打包

无法将静态资源编译打包进二进制文件的话,通常会有两种解决方法:

第一种是识别这类静态资源,是否需要跟着程序走。

第二种就是考虑将其打包进二进制文件中。

第二种情况的话,Go 以前是不支持的,大家就会去借助各种花式的开源库,例如:go-bindata/go-bindata 来实现。

但从在 Go1.16 起,Go 语言自身正式支持了该项特性

演示代码:

import _ "embed"

//go:embed hello.txt

var s string

func main() {

print(s)

}

首先在对应目录下创建hello.txt文件并写"Hello world"

在代码中编写了最为核心的 //go:embed hello.txt 注解。注解的格式很简单,就是 go:embed 指令声明,外加读取的内容的地址,可支持相对和绝对路径。

输出结果:

Hello world

读取到静态文件中的内容后自动赋值给了变量 s,并且在主函数中成功输出。

而针对其他的基础类型,Go embed 也是支持的:

//go:embed hello.txt

var s string

//go:embed hello.txt

var b []byte

//go:embed hello.txt

var f embed.FS

func main() {

print(s)

print(string(b))

data, _ := f.ReadFile("hello.txt")

print(string(data))

}

输出结果:

Hello world

Hello world

Hello world

我们同时在一个代码文件中进行了多个 embed 的注解声明。

并且针对 string、slice、byte、fs 等多种类型进行了打包,也不需要过多的处理,非常便利。

 2.拓展用法:

除去基本用法完,embed 本身在指令上也支持多种变形:

//go:embed hello1.txt hello2.txt

var f embed.FS

func main() {

data1, _ := f.ReadFile("hello1.txt")

fmt.Println(string(data1))

data2, _ := f.ReadFile("hello2.txt")

fmt.Println(string(data2))

}

在指定 go:embed 注解时可以一次性多个文件来读取,并且也可以一个变量多行注解:

//go:embed hello1.txt

//go:embed hello2.txt

var f embed.FS

也可以通过在注解中指定目录 helloworld,再对应读取文件:

//go:embed helloworld

var f embed.FS

func main() {

data1, _ := f.ReadFile("helloworld/hello1.txt")

fmt.Println(string(data1))

data2, _ := f.ReadFile("helloworld/hello2.txt")

fmt.Println(string(data2))

}

同时既然能够支持目录读取,也能支持贪婪模式的匹配:

//go:embed helloworld/*

var f embed.FS

embed.FS 也能调各类文件系统的接口,其实本质是 embed.FS 实现了 io/fs 接口

3.只读属性

在 embed 所提供的 FS 中,其实可以发现都是打开和只读方法:

    type FS

    func (f FS) Open(name string) (fs.File, error)

    func (f FS) ReadDir(name string) ([]fs.DirEntry, error)

    func (f FS) ReadFile(name string) ([]byte, error)

根据此也可以确定 embed 所打包进二进制文件的内容只允许读取,不允许变更。

更抽象来讲就是在编译期就确定了 embed 的内容,在运行时不允许修改,保证了一致性。


本文来自:简书

感谢作者:陈氏美

查看原文:go特性总结(1.13~1.16)

返回前面的内容

相关阅读 >>

Golang语言学习之什么是Golang

手撸Golang Go与微服务 chatserver之2

Go1.16 embed 实践

Go 字符串常用的系统函数

Golang怎么通过cGo调用c++程序

Go语言标准库之strconv

最新字节跳动面试题与岗位层级,绩效考核制度介绍

写在 dubbo Go 的第五个年头

Golang influxdb 基础入门

用 wasmedge 中的嵌入式 webassembly 函数扩展 Golang 应用程序

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...