为了账号安全,请及时绑定邮箱和手机立即绑定
慕课专栏

目录

索引目录

改善Go语言编程质量的50个有效实践

原价 ¥ 78.00

立即订阅
01 Go 语言的前生今世
更新时间:2020-09-08 09:34:16
我要扼住命运的咽喉,它妄想使我屈服,这绝对办不到。生活是这样美好,活他一千辈子吧!——贝多芬

1. Go 语言的诞生

2007 年 9 月 20 日的下午,在谷歌山景城总部的一间办公室里,谷歌的大佬级程序员 Rob Pike1在等待一个 C++ 项目构建的过程中和谷歌的另外两个大佬级程序员 Robert Griesemer2和 Ken Thompson3进行了一次有关设计一门新编程语言的讨论4

图片描述

图1-1-1 Go语言之父(从左到右分别是Robert Griesemer、Rob Pike和Ken Thompson)

当时的谷歌内部主要使用 C++ 语言构建各种系统,但 C++ 的巨大复杂性、编译构建速度慢以及在编写服务端程序时对并发支持的不便等让三位大佬产生了重新设计一门新编程语言的想法。在他们的初步构想中,这门新语言应该是能够给程序员带来快乐、匹配未来硬件发展趋势并适合用来开发谷歌内部大规模程序的。

趁热打铁!在第一天的简短讨论后,第二天这三位大佬又在总部的一间名为 Yaounde 的会议室里进行了一场有关这门新语言具体设计的会议。会后的第二天,Robert Griesemer 发出了一封题为“prog lang discussion”的电邮,这封电邮便成为了这门新语言的第一版设计稿,三位大佬在这门语言的一些基础语法特性上达成了初步一致。

9 月 25 日,Rob Pike 在一封回复电邮中把这门新编程语言命名为 Go:

    Subject: Re: prog lang discussion
    From: Rob 'Commander' Pike
    Date: Tue, Sep 25, 2007 at 3:12 PM
    To: Robert Griesemer, Ken Thompson

    i had a couple of thoughts on the drive home.

    1. name

    'go'. you can invent reasons for this name but it has nice properties.
    it's short, easy to type. tools: goc, gol, goa. if there's an interactive
    debugger/interpreter it could just be called 'go'. the suffix is .go
    ...

2. Go 语言的早期团队和演化历程

经过早期讨论,三位 Go 作者在语言设计上达成初步一致后,便开启了 Go 语言迭代设计和实现的过程。

2008 年初,Unix 之父 Ken Thompson 实现了第一版 Go 编译器,用于验证之前的设计。这个编译器先将 Go 代码转换为 C 代码,再由 C 编译器编译成二进制文件。

到 2008 年中旬,Go 的第一版设计基本结束了。这时,同样在谷歌工作的 Ian Lance Taylor 给 Go 语言的三位作者发了一封电子邮件。他宣称为 Go 语言实现了一个 gcc 的前端,这也是 Go 语言的第二个编译器。

    Subject: A gcc frontend for Go
    From: Ian Lance Taylor
    Date: Sat, Jun 7, 2008 at 7:06 PM
    To: Robert Griesemer, Rob Pike, Ken Thompson

    One of my office-mates pointed me at http://.../go_lang.html .  It
    seems like an interesting language, and I threw together a gcc
    frontend for it.  It's missing a lot of features, of course, but it
    does compile the prime sieve code on the web page.

随后,Ian Lance Taylor 正式加入 Go 语言开发团队,并在后面的 Go 语言发展演进进程中成为了 Go 语言及工具设计和实现的核心人物之一。Russ Cox 也是在 2008 年加入到刚成立不久的 Go 语言开发团队的。随着他的加入,他的一些天赋也随即在语言设计和实现中展现出来,比如io.Readerio.Writer接口,奠定了所有 I/O 库的整体结构。

3. Go 语言正式公布并开源

2009 年 10 月 30 日,Rob Pike 在 Google Techtalk 上做了一次有关 Go 语言的演讲《The Go Programming Language》,这也是 Go 语言第一次公之于众。

Go 语言项目在 2009 年 11 月 10 日正式开源,之后这一天被官方定位 Go 语言的诞辰。最初 Go 语言项目托管在 code.google.com 上,几年后迁移至 GitHub。

随着 Go 语言的开源,Go 语言吸引了全世界开发者的目光。再加上 Go 三位作者在业界的影响力以及谷歌这座大树的加持,更多有才华的程序员加入到 Go 开发团队中,更多贡献者开始为 Go 语言项目添砖加瓦。于是,Go 在发布的当年(2009 年)就成为了著名编程语言排行榜 TIOBE 的年度最佳编程语言。

Go 发布后就吸引了一些公司,尤其是云计算领域的初创公司成为了 Go 语言的早期接纳者。在经过若干年的磨合后,在这些公司中诞生了不乏像 Docker、Kubernetes 这样的“杀手级”或示范性项目,这些项目也让 Go 被誉为“云计算基础设施新兴语言”或直接称为云计算语言。

在发布后,Go 语言也拥有了自己的“吉祥物” - 一只由 Rob Pike 夫人Renee French设计的地鼠,从此地鼠称为了世界各地 Go 程序员的象征。Go 程序员也被称为 Gopher,Go 语言官方技术大会也被称为 GopherCon。

图片描述

图1-1-2 Go语言吉祥物

4. Go 语言的版本

和绝大多数编程语言相似,Go 语言也是“站在巨人的肩膀上的”,正如下图所示,Go 继承了诸多编程语言的特性。

图片描述

图1-1-3 Go的先祖

Go 的基本语法参考了 C 语言,是“C 家族语言”的一个分支;而 Go 的声明语法、包概念则受到了 Pascal/Modula/Oberon 的启发;一些并发的思想则来自于受到了 Tony Hoare 教授 CSP 理论影响的语言,比如:Newsqueak 和 Limbo。

2009 年 11 月 10 日,Go 语言正式对外发布并开源。之后,Go 语言在一段时间内采用了“Weekly Release”的模式,即每周发布一个版本。目前我们在 github.com 上 golang 的官方仓库中仍能找到的早期的 Weekly Release 版本,比如:“weekly.2009-11-06”。

  • 从 2011 年 3 月 7 日开始,除了 Weekly Release,Go 项目还会每月发布一个 Release,即 Monthly Release,比如:release.r56,这种情况一直延续到 Go 1.0 版本发布之前;

  • 2012 年 3 月 28 日,Go 1.0 正式发布。同时 Go 官方发布 Go1 兼容性承诺:只要符合 Go1 语言规范的源代码,Go 编译器将保证向后兼容;

  • 2013 年 5 月 13 日,Go 1.1 版本发布。主要的变动点包括:

    • 新增"method value"语法:允许将方法绑定到一个特定的 receiver 实例上,从而形成一个 function;
    • 引入"terminating statement"的概念;
    • 将 64 位平台上的 int 类型的字节数升为 8 个字节,即 64 bits;
    • 更为精确的 Heap GC,尤其是针对 32 位平台。
  • 2013 年 12 月 1 日,Go 1.2 版本发布。从 Go 1.2 开始,Go 开发组启动了“以每 6 个月为一个发布周期”的发布计划。Go 1.2 版本主要的变动点包括:

    • 增加 Full slice expression: a[low : high : max];
    • 实现抢占式 Goroutine 调度器;
    • go test 新增-cover 标志用于计算测试覆盖率。
  • 2014 年 6 月 18 日,Go 1.3 版本发布。主要的变动点包括:

    • 支持更多平台:solaris,dragonfly,plan9,nacl 等;
    • goroutine 的 stack 模型从”segmented“ model 改为了“contiguous” model,改善“Hot stack split”问题;
    • 更为精确的 Stack GC。
  • 2014 年 12 月 10 日,Go 1.4 版本发布。Go 1.4 也是最后一个编译器和 runtime 由 C 语言实现的版本。其主要的变动点包括:

    • 新增 for range x {…}形式的 for-range 语法;
    • 使用 Go 语言替换 runtime 的部分 C 语言实现,这使得 GC 变得全面精确;
    • 由于 contiguous stack 的应用,goroutine 的默认栈大小从 8k 减小为 2k;
    • 增加 internal package 机制;
    • 增加"canonical import path"机制;
    • 新增 go generate 子命令;
    • 删除 Go source 树中的 src/pkg/xxx 中 pkg 这一级别,直接使用 src/xxx。
  • 2015 年 8 月 19 日,Go 1.5 版本发布,Go 1.5 版本是一个里程碑式的重要版本。因为从这个版本开始,Go 实现了自举,即无需再依赖 C 编译器。但 Go 编译器的性能照比 Go 1.4 的 C 实现有了较大幅度的下降。其主要的变动点包括:

    • Go 编译器和 runtime 使用 Go 全部重写,原先的 C 实现被彻底移除;
    • 跨平台编译 Go 程序更为简洁,只需设置两个环境变量即可:GOARCH 和 GOOS;
    • 支持 map literal;
    • GOMAXPROCS 的初始默认值由 1 改为了运行环境的 CPU 核数;
    • 大幅度优化 GC 延迟,多数情况 GC "Stop The World"的时间低于 10ms;
    • 增加 vendor 机制,改善 Go 包依赖管理;
    • 增加 go tool trace 子命令;
    • go build 增加-buildmode 命令选项,支持将 Go 代码编译为 shared library 形式。
  • 2016 年 2 月 17 日,Go 1.6 版本发布。主要的变动点包括:

    • 进一步优化 GC 延迟,实现 Go 程序在占用大内存的情况下,GC 延迟仍低于 10ms;
    • 自动支持 HTTP/2;
    • 定义了在 C 代码中共享 Go 指针的规则。
  • 2016 年 8 月 15 日,Go 1.7 版本发布。主要的变动点包括:

    • 针对 x86-64,实现了 SSA 后端,使得编译出的 binary file size 减少 20%~30%;运行效率提升 5%~35%;
    • Go 编译器的性能比 Go 1.6 版本提升近一倍;
    • go test 支持 subtests 和 sub-benchmarks;
    • 标准库新增 context 包。
  • 2017 年 2 月 16 日,Go 1.8 版本发布。主要的变动点包括:

    • 支持仅 tags 不同的两个 struct 可以相互做显式类型转换;
    • 标准库增加 sort.Slice 函数;
    • 支持 HTTP/2 Push 机制;
    • 支持支持 HTTP Server 优雅退出;
    • 增加了对 Mutex 和 RWMutex 的 profiling(剖析)支持;
    • 支持 Go plugins,增加 plugin 包;
    • 支持默认的 GOPATH 路径,无需再显式设置;
    • 进一步优化 SSA 后端代码,程序平均性能提升 10%左右;
    • Go 编译器性能进一步提升,平均编译链接的性能提升幅度在 15%左右;
    • GC 的延迟进一步降低,STW(Stop The World)的时间通常将低于 100 微秒,甚至经常低于 10 微秒。
    • 优化 defer 的实现,使得其性能损耗降低了一半。
  • 2017 年 8 月 25 日,Go 1.9 版本发布。主要的变动点包括:

    • 新增了 type alias 语法;
    • 在原先的支持包级别的并发编译的基础上又实现了包函数级别的并发编译,使得 Go 编译性能有 10%左右的提升;
    • 大内存对象分配性能得到显著提升;
    • 增加了对 monotonic clock 的支持;
    • 提供了一个支持并发的 Map 类型:sync.Map;
    • 增加 math/bits 包,将高性能位操作实现收入标准库。
  • 2018 年 2 月 17 日,Go 1.10 版本发布。主要的变动点包括:

    • 支持默认 GOROOT,开发者无需显式设置 GOROOT 环境变量;
    • 增加 GOTMPDIR 环境变量;
    • 通过 cache 大幅提升构建和 go test 执行的性能,并基于源文件内容是否变化判定是否使用 cache 结果;
    • 支持 Unicode 10.0 版本。
  • 2018 年 8 月 25 日,Go 1.11 版本发布Go 1.11Russ CoxGopherCon 2017 大会上发表 "Toward Go2"之后的第一个 Go 版本,新的 Go 包管理机制 go modules 在 Go 1.11 中的落地为后续“Go2”的提议渐进落地奠定了良好的基础。该版本主要的变动点包括:

    • 引入 Go module,为 Go 包管理提供了创新性的解决方案;
    • 引入对 WebAssembly 的支持,让 Gopher 可以使用 Go 语言来开发前端 Web 应用;
    • 为调试器增加了一个新的实验功能:允许在调试过程中动态调用 Go 函数。
  • 2019 年 2 月 25 日,Go 1.12 版本发布。主要的变动点包括:

    • 对 Go 1.11 中增加的 go modules 机制做了进一步优化;
    • 增加对 TLS 1.3 的支持;
    • 优化了大量 heap 依然存在时 GC 清理环节的性能,使得一次 GC 后的内存分配延迟得以改善;
    • 运行时更加积极地将释放的内存归还给操作系统,以应对大块内存分配无法重用已存在的堆空间的问题;
    • 该版本中 Build cache 成为必需。
  • 2019 年 9 月 4 日,Go 1.13 版本发布。主要的变动点包括:

    • 增加以 0b 或 0B 开头的二进制数字字面量形式,如:0b111
    • 增加以"0o"或"0O"开头的八进制数字字面量形式,如:0o700
    • 增加以 0x 或 0X 开头的十六进制形式的浮点数字面量,如:0x123.86p+2
    • 支持在数字字面量中通过数字分隔符"_"提高可读性,如:a := 5_3_7等;
    • 取消了移位操作(>><<)的右操作数仅能是无符号数的限制;
    • 继续对 Go module 机制进行优化,包括:当GO111MODULE=auto时,无论是在$GOPATH/src 下还是 GOPATH 之外的仓库中>,只要目录下有 go.mod,Go 编译器都会使用 go module 来管理依赖;GOPROXY 支持配置多个代理,默认值为https://proxy.golang.org,direct;提供了 GOPRIVATE 变量,用于指示哪些仓库下的 module 是私有的,即不需要通过 GOPROXY 下载,也不需要通过 GOSUMDB 去验证其校验和;
    • Go 错误处理改善:在标准库中增加 errors.Is 和 As 函数来解决 error value 的比较判定问题;增加 errors.Unwrap 函数来解决 error 的展开(unwrap)问题;
    • defer 性能提升 30%;
    • 支持的 unicode 标准从 10.0 版本升级到Unicode 11.0 版本
  • 2020 年 2 月 26 日,Go 1.14 版本发布。主要的变动点包括:

    • 支持嵌入接口的方法集可重叠
    • 支持 goroutine 的异步抢占式调度;
    • defer 性能得到大幅优化,理论上可实现 30%的提升;
    • 重新实现了运行时(runtime)层的 timer;
    • go module 可应用于生产环境;
    • 标准库 testing 包增加Cleanup方法用于实现测试执行后的清理工作。

Go 团队发布的 Go 语言稳定版本总体质量一直很高,少有影响使用的重大 bug。Go 团队一直建议大家使用最新的发布版。Google 的产品,比如 Google App Engine(以下简称为 GAE),向来是这方面的“急先锋” —— 一般在 Go 新版本发布不久后,GAE 便会宣布支持最新版本的 Go。

Go 团队每年发布两次大版本,一般是在二月份和八月份发布。Go 团队对最新的两个 Go 稳定版本提供支持,比如如果目前最新版本是 Go 1.14,那么 Go 团队会为 Go 1.14 和 Go 1.13 提供支持。如果 Go 1.15 版本发布,支持的版本将变成 Go 1.15 和 Go 1.14。支持的范围主要包括修复版本中存在的重大问题、仅文档变更以及安全问题更新。

5. 小结

了解一门编程语言的诞生历史和早期演化史,有助于程序员在学习这门语言的同时产生或加深对语言的认同感。程序员能感同身受,从而能更加有热情的投入到这门语言的学习和使用当中。同时这种认同感也能积极推动程序员在后续的实践中形成语言的原生思维(比如:Go 语言思维),从而更加高效地利用这门语言进行编程,解决实际问题。

同时,了解 Go 版本的发布历史以及不同版本的主要变动点,将有助于程序员根据自身的实际情况选择最为适合的 Go 版本。


  1. Rob Pike(罗伯·派克),早期贝尔实验室成员,参与了 Plan9 操作系统、C 编译器以及多种语言编译器的设计和实现,UTF-8 发明人之一。 ↩︎

  2. Robert Griesemer(罗伯特·格瑞史莫),Java 的 HotSpot 虚拟机和 Chrome 浏览器的 JavaScript V8 引擎的设计者之一。 ↩︎

  3. Ken Thompson(肯·汤普逊),图灵奖得主、Unix 之父以及 C 语言的发明人之一。 ↩︎

  4. “少即是多” - https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html ↩︎

}
立即订阅 ¥ 78.00

你正在阅读课程试读内容,订阅后解锁课程全部内容

千学不如一看,千看不如一练

手机
阅读

扫一扫 手机阅读

改善Go语言编程质量的50个有效实践
立即订阅 ¥ 78.00

举报

0/150
提交
取消