Go1.14新特性
前述
Go1.14已经发布有一阵子了,下面是我学习的过程,也是知识的整理.
下载地址
详细说明
可查看本地安装之后的doc文件夹,里面包含了go1.14.html
文件,直接在本地打开即可食用
哪些地方做了优化
工具层面
go module
1.Flags-modfile=file
,现在可以直接指定mod file了,不在是固定的项目目录下了
语言&&Runtime
聚合接口
在1.14之前,我们聚合接口的定义不能有相同签名和返回值的方法,但是1.14取消了这个限制.
type Foo interface {
GetName() string
SetFooName(string)
}
type Bar interface {
GetName() string
SetBarName() string
}
type FooBar interface {
Foo
Bar
}
defer零消耗
1.14之前,defer的runtime要维护堆栈,我们要压栈和出栈的性能损耗,但是现在在编译器层做了defer的优化
goroutine 基于信号的抢占式调度实现
经典的例子,设置p
为1,这样规避并发,然后go协程初始化到队列等待runtime调度,然而for循环的存在,调度一直调度不到,然后现在1.14可以利用信号机制抢占for的循环资源了.
package main
import (
"runtime"
)
func main() {
runtime.GOMAXPROCS(1)
go func() {
panic("already call")
}()
for {
}
}
思考的问题:抢占是否影响性能 ?
抢占通常是由于阻塞性系统调用引起的,例如磁盘io,cgo等,所以一定程度上很难形成协程阻塞调度.
计时器优化,内存分配器
这里主要学习 这篇文章
包层面
test包
func TestIt(t *testing.T) {
t.Cleanup(func() {
fmt.Println("clean up 一些事情")
})
t.Log("这是个log")
time.Sleep(time.Second * 2)
}
go1.14 会在sleep之前就输出log日志.同时增加了Cleanup方法(这个方法很有用,在我们测试完一个用例后可以做一些善后处理,例如rollback db等)
hash/maphash
降低了hash冲突的可能性.
json
InputOffset()方法
func main() {
const jsonStr = `
{"Job":"java", "Name":"zhangsan"}
{"Job":"php", "Name":"lisi"},
{"Job":"python", "Name":"wangwu"}
`
type Jober struct {
Job, Name string
}
dec := json.NewDecoder(strings.NewReader(jsonStr))
for {
var m Jober
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
fmt.Println(m)
fmt.Println(dec.InputOffset())
}
}
输出:
{java zhangsan}
36
{php lisi}
67
2020/03/11 21:39:57 invalid character ',' looking for beginning of value
这里我特意后面多加了',', 可以看的出来解析的时候加入InputOffset很有用
ioutil
TempDir 第二个参数,随机字符串模式,可以是一个更加定制化的模式
ioutil.TempDir("/tmp/ss", "name-date-*-test")
这里的 * 可以在一个字符串模式的中间
log
logger = log.New(&buf, "logger", log.Lmsgprefix)
文件名和行数放在prefix的前面
reflect包
StructOf 可以设置私有字段了
func main() {
typ := reflect.StructOf([]reflect.StructField{
{
Name: "Height",
Type: reflect.TypeOf(float64(0)),
Tag: `json:"height"`,
},
{
Name: "age",
Type: reflect.TypeOf(int(0)),
Tag: `json:"age"`,
PkgPath: "other/path",
},
})
v := reflect.New(typ).Elem()
v.Field(0).SetFloat(0.4)
v.Field(1).SetInt(2)
s := v.Addr().Interface()
w := new(bytes.Buffer)
if err := json.NewEncoder(w).Encode(s); err != nil {
panic(err)
}
fmt.Printf("value: %+v\n", s)
fmt.Printf("json: %s", w.Bytes())
r := bytes.NewReader([]byte(`{"height":1.5,"age":10}`))
if err := json.NewDecoder(r).Decode(s); err != nil {
panic(err)
}
fmt.Printf("value: %+v\n", s)
}
这里的age
是一个不可导出的私有属性,但是我同样可以在structof中设置
runtime
Goexit()
func todo() {
defer func() {
fmt.Println(recover())
}()
defer panic("cancelled goexit")
runtime.Goexit()
}
func main() {
todo()
}
这里在1.13中,由于goexit退出当前协程而报错,但是此时会被recover住,但是1.14将会顺利报错.
strconv包
strconv包中的NumError类型现在支持is判断不同的类型,代码如下:
str := "hello world"
if _, err := strconv.ParseFloat(str, 64); err != nil {
if errors.Is(err, strconv.ErrSyntax) {
}
if errors.Is(err, strconv.ErrRange) {
}
}
评论已关闭