Go 技术专题简介-冯金伟博客园

简介

此条目或章节需要时常更新。内容可能会随着时间而有所变化或进展。未有可靠来源的臆测内容可能会被移除。若您发现有尚未更新的内容,欢迎您编辑更新。Go编程范型编译型,可平行化,结构化,指令式设计者罗伯特·格瑞史莫(英语:Robert Griesemer)罗勃·派克肯·汤普逊实现者Google发行时间2009年11月10日,​13年前​(2009-11-10)当前版本1.20.2 (2023年3月7日;稳定版本) 操作系统Linux、macOS、FreeBSD、Windows许可证BSD文件扩展名.go网站https://go.dev/https://golang.google.cn/主要实现产品gc (8g,6g,5g),gccgo启发语言C、Oberon、Limbo、Newsqueak Gopher(囊鼠科),Go的吉祥物Go(又称Golang)是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。罗伯特·格瑞史莫(英语:Robert Griesemer)、罗勃·派克及肯·汤普逊于2007年9月开始设计Go,稍后伊恩·兰斯·泰勒(Ian Lance Taylor)、拉斯·考克斯(Russ Cox)加入项目。Go是基于Inferno操作系统所开发的。Go于2009年11月正式宣布推出,成为开放源代码项目,支持Linux、macOS、Windows等操作系统。在2016年,Go被软件评价公司TIOBE(英语:TIOBE Programming Community Index)选为“TIOBE 2016年最佳语言”。目前,Go每半年发布一个二级版本(即从a.x升级到a.y)。

描述

Go的语法接近C语言,但对于变量的声明(英语:type declaration)有所不同。Go支持垃圾回收功能。Go的并行计算模型是以东尼·霍尔的通信顺序进程(CSP)为基础,采取类似模型的其他语言包括Occam和Limbo,Go也具有这个模型的特征,比如通道传输。通过goroutine和通道等并行构造可以建造线程池和管道等。在1.8版本中开放插件(Plugin)的支持,这意味着现在能从Go中动态加载部分函数。

与C++相比,Go并不包括如枚举、异常处理、继承、泛型(此功能在go1.18中加入)、断言、虚函数等功能,但增加了切片(Slice) 型、并发、管道、垃圾回收、接口等特性的语言级支持。对于断言的存在,则持负面态度,同时也为自己不提供类型继承来辩护。

不同于Java,Go原生提供了关联数组(也称为哈希表(Hashes)或字典(Dictionaries))。

批评

尽管 Go 的官方与支持者对于语言中不使用泛型与异常处理有着大量辩解说词,但批评声也从未停过。在发表 Go 语言 2.0 的草案时,官方称没有泛型、异常处理与模块对于 Go 发展造成很大的阻碍,等同承认 Go 没有这些特色是设计错误。

Go 的垃圾回收机制一直被人诟病,直到 1.8 版本垃圾回收的功能才较为稳定。然而尽管如此,Go 的垃圾回收还是远远落后 JVM 的 G1 和 ZGC。Discord 的研发团队在2020年初甚至发表一篇博客,宣布把其中一个服务由从 Go 转移至 Rust,理由是 Go 的垃圾回收会导致每2分钟出现一次卡顿,并且 Discord 研发团队测试了 Go 语言的1.8、1.9、1.10版本这个问题都没有改善。

历史

2007年,Google设计Go,目的在于提高在多核、网络机器(networked machines)、大型代码库(codebases)的情况下的开发效率。当时在Google,设计师们想要解决其他语言使用中的缺点,但是仍保留他们的优点。

静态类型和运行时效率。(如:C++)

可读性和易用性。(如:Python 和 JavaScript)

高性能的网络和多进程。

设计师们主要受他们之间流传的“不要像C++”启发。

Go于2009年11月正式宣布推出,版本1.0在2012年3月发布。之后,Go广泛应用于Google的产品以及许多其他组织和开源项目。

在2016年11月,Go(一种无衬线体)和Go Mono 字体(一种等宽字体)分别由设计师查尔斯·比格洛(英语:Charles Bigelow)和克莉丝·荷姆斯(英语:Kris Holmes)发布。两种字体均采用了WGL4(英语:WGL4),并且依照着 DIN 1450 标准,可清晰地使用了 large x-height 和 letterforms 。

在2018年8月,本地的图标更换了。待描述完整 然而,Gopher mascot 仍旧命相同的名字。

在2018年8月,Go的主要贡献者发布了两个关于语言新功能的“草稿设计——泛型和异常处理,同时寻求Go用户的反馈。Go 由于在1.x时,缺少对泛型编程的支持和冗长的异常处理而备受批评。

版本历史

主要版本 发布日期 语言改动 其他改动

1 – 1.0.32012-03-28初始版本1.11 – 1.11.62018-08-24无

Go 1.11 增加 WebAssembly 的实验入口。

Go 1.11 初次支持新概念 “modules”, 是 GOPATH 的代替品,并支持版本和包的分发。

Go 1.11 停止支持 Windows XP 和 Windows Vista.

Go 1.11.3 以及之后会修复 crypto/x509 包的 TLS 认证的脆弱性(vulnerability) .

1.12.12019-02-25无

Opt-in 对TLS 1.3支持

增强对 modules 的支持( 为在 Go 1.13 称为默认选择做准备)

支持 windows/arm

提高 macOS & iOS 向前兼容

1.13.12019-09-03

Go 已支持更统一和更现代化的 数字字义前缀 (number literal(英语:Integer literal) prefixes )

crypto/tls 包中对TLS 1.3 的支持。(Go 1.14 将会移除 opt-out)

支持错误包装(Error wrapping)

代码示例

Hello World

下面是用Go写成的Hello World程序:

package mainimport "fmt"func main() {    fmt.Println("Hello, World")}

HTTP网页服务器

透过Go仅需几行代码就完成HTTP网页服务器的实现:

package mainimport (    "io"    "net/http")func hello(w http.ResponseWriter, r *http.Request) {    io.WriteString(w, "Hello world!")}func main() {    http.HandleFunc("/", hello)    http.ListenAndServe(":8000", nil)}

Echo命令程序

下面的例子说明了怎样用Go去实现一个像Unix中的Echo命令程序:

package mainimport (    "os"    "flag")var omitNewline = flag.Bool("n", false, "don't print final newline")const (    Space   = " "    Newline = "n")func main() {    flag.Parse() // Scans the arg list and sets up flags    var s string = ""    for i := 0; i  0 {            s += Space        }        s += flag.Arg(i)    }    if !*omitNewline {        s += Newline    }    os.Stdout.WriteString(s)}

语言特征

撰写风格

Go有定义如下的撰写风格:

每行程序结束后不需要撰写分号;

大括号{不能够换行放置。

if判断式和for循环不需要以小括号包覆起来。

使用 tab 做排版

除了第二点外(换行会产生编译错误),在不符合上述规定时,仍旧可以编译,但使用了内置gofmt工具后,会自动整理代码,使之符合规定的撰写风格。

项目架构

module

Go 采用 module 的概念(于 go1.11才开始激活,旧版本请参阅工作区),每个项目都是一个 module ,而每个 module 底下会有个 go.mod 的文件,来管理该 module 所引用的外部库、开发版本……等等。

一个 module 的文件夹目录可能如下

go.mod hello/     hello.go                 outyet/     main.go                      main_test.go           # 测试用的程式 stringutil/     reverse.go                  reverse_test.go        #  测试用的程式 bmp/     reader.go                   writer.go               

然后 go.mod 的内容可能为

module example.org/go-mod-samplego 1.11require (github.com/golang/example v0.0.0-20220412213650-2e68773dfca0 golang.org/x/image v0.1.0 )

工作区

Go的工作区(英语:Workspace)位于GOPATH,其目录结构如下:

srcpkgbin

三个目录的用途分别为

目录 用途

src引用的外部库pkg编译时,生成的对象文件(英语:Object file)bin编译后的程序

举例来说,整个项目目录可能会如下:

bin/    hello                          # 生成的执行档    outyet                         # 生成的执行档pkg/    linux_amd64/        github.com/golang/example/            stringutil.a           # 编译时,生成的对象档案src/    github.com/golang/example/        .git/                      # 外部 Git 库的诠释资料 hello/     hello.go               # Git 库的程式码 outyet/     main.go                # Git 库的程式码     main_test.go           # Git 库的程式码(测试用的程式) stringutil/     reverse.go             # Git 库的程式码     reverse_test.go        # Git 库的程式码(测试用的程式)    golang.org/x/image/        .git/                      # 外部 Git 库的诠释资料 bmp/     reader.go              # Git 库的程式码     writer.go              # Git 库的程式码

轻型协程

Go的主要特色在于易于使用的并行设计,叫做Goroutine,透过Goroutine能够让程序以异步的方式执行,而不需要担心一个函数导致程序中断,因此Go也非常地适合网络服务。假设有个程序,里面有两个函数:

func main() {    // 假设 loop 是一个会重複执行十次的迴圈函式。    // 迴圈执行完毕才会往下执行。    loop()    // 执行另一个迴圈。    loop()}

这个时候透过Go让其中一个函数同步执行,如此就不需要等待该函数执行完后才能执行下一个函数。

func main() {    // 透过 `go`,我们可以把这个函式同步执行,    // 如此一来这个函式就不会阻塞主程式的执行。    go loop()    // 执行另一个迴圈。    loop()}

Goroutine是类似线程的概念,属于纤程(区别于协程和线程)。线程属于系统层面,通常来说创建一个新的线程会消耗较多的资源且管理不易;而协程的主要作用是提供在一个线程内的并发性,却不能利用多个处理器线程。而 Goroutine就像轻量级的线程,一个Go程序可以执行超过数万个 Goroutine,并且这些性能都是原生级的,随时都能够关闭、结束,且运行在多个处理器线程上。一个核心里面可以有多个Goroutine,透过GOMAXPROCS参数你能够限制Gorotuine可以占用几个系统线程来避免失控。

在内置的官方包中也不时能够看见Goroutine的应用,像是net/http中用来监听网络服务的函数实际上是创建一个不断执行循环的Goroutine;同时搭配了epoll 等IO多路复用机制维护Goroutine的事件循环。

编译器

当前有两个Go编译器分支,分别为官方编译器gc和gccgo。官方编译器在初期使用C写成,后用Go重写从而实现自举(英语:Bootstrapping (compilers))。Gccgo是一个使用标准GCC作为后端的Go编译器。

官方编译器支持跨平台编译(但不支持CGO),允许将源代码编译为可在目标系统、架构上执行的二进制文件。