Go实践笔记

栏目: IT技术 · 发布时间: 4年前

内容简介:目前在用go写同步对战服务器这块。这门编程语言很久以前虽然学过,不过时间久了也用的较少,时间久了忘了很多。在写服务器过程中,可以享受到go对高性能并发的内置支持,但同时也会遇到一些坑,这里针对go的一些要点和问题,记录一些笔记和坑的解决办法。(1)go的异常处理通过(2)go的异常不像java那样区分类型,包括官方的标准库也是,更多的是通过运行时错误码来判断。处理异常的时候,代码维护和可读方面没有java那么直观。

Go实践笔记

目前在用 go 写同步对战服务器这块。这门编程语言很久以前虽然学过,不过时间久了也用的较少,时间久了忘了很多。在写服务器过程中,可以享受到go对高性能并发的内置支持,但同时也会遇到一些坑,这里针对go的一些要点和问题,记录一些笔记和坑的解决办法。

1.go语言的异常处理机制不够完善。

(1)go的异常处理通过 defer recover 来实现,不支持函数局部代码块异常处理。

(2)go的异常不像 java 那样区分类型,包括官方的标准库也是,更多的是通过运行时错误码来判断。处理异常的时候,代码维护和可读方面没有java那么直观。

针对这一点,我思考了下,实际上因为go支持闭包 可以通过闭包来代替代码块处理局部异常;其次 go panic 抛出的异常可以是任意类型的,而go支持通过反射来动态判断类型,所以可以通过这两点模拟go版的区分类型的 try catch 机制。

2.go语言内置的一些数据类型都不是线程安全的。

go语言内置的一些数据类型支持,包括 slicemaplist 等都存在线程安全问题。对于大部分通用场景来说,使用go内置的 读写锁 即可满足需求;不过也可以使用 channel 代替锁,通过 channel 协调读写线程,实现线程安全。

参考相关文章: https://www.jianshu.com/p/df973e890663

3.go语言不支持宏,不支持泛型,也不支持重载

go语言为了加快编译速度,目前都没有支持宏和泛型,甚至不支持重载,而内置的数学库也只是支持float64型,对于复杂的业务场景来说,非常不便。

4.数据类型转换不方便

由于go语言不支持泛型,而go对于非基本数据类型的强制类型转换也没有提供专门的支持,在转换非基本数据类型时,显得很繁琐。例如下面这个例子,正确的转换很冗长,也无法使用宏简化(因为go也不支持宏):

` type _Int32 int32

var a *[]_Int32
var b *[]int32=(*[]int32)(a) // 语法报错
var d *[]int32=(*[]int32)(unsafe.Pointer(a)) //能正确转换

`

同样的,结构体的串行化也不方面,显得冗长,存在性能消耗:

`

type Struct struct{
    a int64
    b int32
}

sample:=Struct{}

size:=int(unsafe.Sizeof(&sample))
sh := &reflect.SliceHeader{
    Data: uintptr(unsafe.Pointer(&sample)),
    Len:  size,
    Cap:  size,
}

// 结构体->[]byte
data := *(*[]byte)(unsafe.Pointer(sh))

// []byte->结构体
originStruct:=(*Struct)(unsafe.Pointer(&data));

`

5.对goroutine调度控制较弱,需要注意安排好不同goroutine之间的依赖关系

6.关于 go版本的protobuf的性能问题

最近对比了messagepack和protobuf在go上面的性能表现。

序列化耗时 反序列化耗时 序列化数据大小
原始数据 \ \ 180
protobuf 100 120 45
messagepack 200 240 210

protobuf在性能上确实非常优越,速度快,体积小;但是也存在一些明显的缺陷:

1.protobuf导出的数据类型内部如果存在嵌套结构,那么嵌套结构都是使用指针关联的,一方面在反序列化过程中会发生大量内存分配操作,容易产生大量内存碎片,对于内存相对较小的机器非常不利,另一方面不利于结构体深拷贝操作,强行深拷贝会存在性能问题。

2.对于所有没有赋值或者赋了默认值的字段,在反序列化过程中都会初始化为空指针或者零值,会存在两个隐患:

  • 1.一旦产生字段赋值操作,很容易出现空指针异常。
  • 2.未赋值的数据结构指针(空指针)依旧可以正常使用,只不过获取的字段值都是空指针或零值,容易误导开发者以为该结构体实例是存在实体的。

7.go中所有的对象传递全部都是值传递,包括所谓的指针引用,实际上也是基于值传递实现的。go初学者稍不注意就会因此引发结构体副本之间的状态同步bug或者产生对一些第三方库(包括官方库)的误用。

比如socket编程中:

`

listener, err := net.Listen("tcp", "127.0.0.1:8001")

if err != nil {
        fmt.Println("err = ", err)
        return
}
defer listener.Close()
for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("err = ", err)
                return
     }
    // 处理用户请求, 新建一个协程
    // 为什么conn不使用指针传递呢,不会产生状态同步问题吗?
    // 这是因为net.Conn类型的数据实际上只保存了一个指针变量 `fd *netFD`,而所有的操作实现都是通过该指针来间接访问,所以无论conn拷贝几份,内部的fd指针值都一样。
    // 这应该是官方为了方便使用特意这样设计的
    // 但这用设计同时会引发另一个问题,就是 (&conn) 指针无法直接使用,虽然目前这不是什么大问题
    // 的
     go HandleConn(conn)

}

`

8.uintptr和gc

使用 uintptr 类型的值保存原始对象指针不会阻止该对象gc,需要结合 runtime.KeepAlive() 来使用,要尽量避免这种用法。

欢迎关注我们的微信公众号,每天学习Go知识

Go实践笔记

以上所述就是小编给大家介绍的《Go实践笔记》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

埃隆·马斯克传

埃隆·马斯克传

陆西 / 重庆出版社 / 2014-7 / 38.00元

埃隆·马斯克(Elon Musk)1971年出生于南非,毕业于美国宾夕法尼亚大学,工程师、企业家、亿万富翁。最成功的全球网上支付平台Paypal创始人之一,现任美国商业航天企业空间探索技术公司(SpaceX)CEO,电动车生产企业特斯拉汽车(Tesla Motors)CEO,光伏发电服务供应企业SolarCity董事长。如今他被视为经济危机后美国创新与科技实力的新象征。 今天PayPal依然......一起来看看 《埃隆·马斯克传》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具