你好, 我是蔡超, 现在是 Mobvista 技术副总裁, 前亚马逊 (中国) 首席软件架构师, 极客时间《Go 语言从入门到实战》视频课程的作者.
在 2018 年的 QCon 北京全球软件开发大会上, 我做了题为《讲给 Java / C++ 开发者的 Go 高效编程》的主题演讲, 会后跟大家交流, 发现许多人对 Go 语言的学习有需求和热情. 但大家的普遍问题就是: 不知道该如何高效的入门 Go 语言, 尤其是有编程基础的开发者, 一不小心就会陷入思维的误区.
因此, 今天我就来梳理一下 Go 语言入门学习的过程中, 你容易产生的五个思维误区, 帮你掌握在 Go 语言与其他编程语言的不同之处.
一, 重新思考面向对象程序的设计和实现
首先, 你要先理解 Go 语言面向对象编程是具有特殊性的. 当然,"Go 是不是支持面向对象" 这本身就是一个值得思考的话题, 官方文档给出的答案是 "Yes and no" . 虽说 Go 语言中是有类型的存在, 也允许面向对象的编程风格, 但却没有类型层次结构.
Go 语言中没有对类型继承提供支持, 而是通过复合来进行扩展, 并通过类型嵌入来简化复合的使用. 很多人会把类型嵌入看成是 Go 中的继承机制, 但是类型嵌入并不支持最基本的继承特性:
子类替换
方法重载(override)
Go 语言中的接口机制与其他语言截然不同, 实现接口的类型完全不依赖于接口定义. 接口作为方法签名的集合, 任何类型的方法集中只要拥有与之对应的全部方法, 就表示它实现了该接口. 只有深刻理解这些, 才能更好发挥 Go 的生产力特性.
不支持继承, 特殊的接口类型, 这些都会要求我们重新思考设计和编程实现.
戳此可获取: Go 语言官方文档
二, 改变传统 GC(垃圾收集器)语言的思维模式
Go 是一个非常特殊的语言, 即追求简单性又追求高效率, Go 既内置支持 GC(垃圾收集器), 又支持指针对内存的直接访问. 其他支持 GC 的语言, 比如在 Java 中, 由于希望对开发者可以屏蔽内存管理, 所以语言中没有提供指针的直接访问. 为了提高数据访问和传递的效率, 编程语言根据不同的情况, 通过约束采用值传递或引用传递, 来减少数据复制.
而 Go 比较简单地统一采用值传递, 但提供指针机制, 因此用户可以自己来选择数据的传递方式, 要引用传递时可以通过传递指针来完成.
所以在编码时, 你要考虑充分指针, 提高数据访问效率, 减少内存复制并编写 GC 友好的代码.
三, 重新思考程序的错误处理机制
Go 语言的错误处理机制中既不支持现在主流的异常模式, 同时也与传统的 C 程序通过返回值返回错误状态不同, Go 语言支持返回多个值, 可以同时返回结果和错误状态.
以下是 Java 语言示例:
- try{
- ...
- }catch(XXException e){
- // 错误处理
- }
以下是 Go 语言示例:
- ifv,err= callSomeFn();err!=nil{
- // 错误处理
- }
Go 的 error 处理方式一直以来都是争论的焦点, 很多开发者认为 Go 的错误处理机制似乎回到了 70 年代, 使得错误处理代码冗长且重复. 而 Go 的设计者则认为 try-catch-finally 的结构导致异常处理与控制流程的耦合, 从而使程序结构发生了混乱.
Go 的设计者当初选择返回值这种错误处理机制, 而不是 try-catch 这种机制, 主要是考虑到前者适用于大型软件, 后者更适合小程序. 因此, 程序变大的时候, try-catch 会让错误处理更加冗长繁琐, 也就容易出错. try-catch-finally 会怂恿程序员标注过多普通错误, 诸如打开文件失败之类的异常, 使得程序更加繁琐.
这就决定了你必须重新思考错误处理的编程模式, 因为这样的代码是 Go 语言中非常常见的.
四, 思考和学习使用 CSP 并发模型
与主流语言通过共享内存来进行并发控制方式不同, Go 语言采用了 CSP 模式. 这是一种用于描述两个独立的并发实体通过共享的通讯 Channel(管道)进行通信的并发模型. 很多使用过 Erlang 等基于 Actor 模式的程序员, 会误认为 Go 和这些语言的模式是一样的.
而实际上, Go 的 CSP 模式与常见的 Actor 模式 (如: Erlang 语言就采用了 Actor 模式) 也有不少差异, 例如:
和 Actor 的直接通讯不同, CSP 模式则是通过 Channel 进行通讯的, 更松耦合一些;
Go 中 channel 是有容量限制并且独立于处理 Groutine, 而如 Erlang,Actor 模式中的 mailbox 容量是无限的, 接收进程也总是被动地处理消息.
Actor 模式和 CSP 模式区别图
所以, 要用好 Go 语言, 一定要思考和学习使用 CSP 来高效的实现我们常见并发任务.
五, 理解 Goroutine 的调度机制
Goroutine 是 Go 语言的招牌特性之一, 较之线程是非常轻量级的. 但是, 如果你不了解其中的机制, 仅仅按照线程的套路来使用, 就发挥不出来 Goroutine 的优势, 甚至还会导致很多性能问题.
Goroutine 有着和 Java 线程完全不同的调度机制, Java 线程模型中线程和 KSE(Kernel space Entity)是 1:1 的关系, 一个用户线程对应一个 KSE. 而 Groutine 和 KSE 是多对多的对应关系. 虽然, Groutine 的调度机制不如, 由内核直接调度的线程机制效率那么高, 但是由于 Groutine 间的切换可以不涉及内核级切换, 所以代价小很多.
CSP 并发模型, Goroutine 的调度机制是 Go 语言入门学习的过程中的重点和难点, 我会在 《Go 语言从入门到实战》进阶篇 中进行详细的讲解, 敬请期待.
拓展阅读:
5 个步骤, 编写你的第一个 Go 程序
来源: http://www.tuicool.com/articles/be26z2n