首页 > 编程笔记 > Go语言笔记 阅读:13

Go语言是什么,Go语言简介(新手必看)

Google 公司有一个传统,允许员工利用 20% 的工作时间开发自己的实验项目。

2007 年 9 月,UTF-8 的设计者之一 Rob Pike(罗布·皮克)在 Google 的分布式编译平台上进行 C++ 编译时,与同事 Robert Griesemer(罗布·格里泽默)在漫长的等待中讨论了编程语言面临的主要问题。他们一致认为,相较于在已经臃肿的语言上不断增加新特性,简化编程语言将会带来更大的进步。

随后,他们又说服 UNIX 的发明人 Ken Thompson(肯·汤普森)一同来为此做点事情。几天后,他们三人启动了名为“Go语言”的开发项目,这标志着 Go 语言的诞生。

Go语言的发展趋势

Go 语言诞生至今已有十多年,越来越多以 Go 语言为基础的成功案例让用户和决策者信心倍增。近年来,热门云原生项目(Docker、Kubernetes、Prometheus等)为 Go 语言的发展提供了很大的帮助。

Go 语言正式发布后,初期虽获得关注,但在 TIOBE Index 中的排名较低。之后,Go 语言逐渐得到开发者和企业的认可,并被应用到云计算、微服务等领域,其在 TIOBE Index 中的排名也上升并稳定在前 20 名左右。值得一提的是在 2009 年和 2016 年,Go 语言被评为年度最佳编程语言。

下图是 TIOBE Index 发布的 Go 语言发展趋势图:


图 1 TIOBE Index发布的Go语言发展趋势图

从该图中可以看出,Go 语言目前处于理性接受阶段。这意味着越来越多的开发者和企业开始认识到 Go语言的优势,并将其应用于各种软件项目中。

Go语言优秀的语言特性

在编写代码时,我们最关心的往往是编码效率,其次是程序的执行效率。

Go语言在编码效率和执行效率方面实现了极佳的平衡,且其代码的设计理念与计算机的运行方式高度契合,因此我们可以准确地判断程序的走向和运行速度,清晰地了解其工作原理。

编码效率与执行效率的平衡、代码的设计理念与计算机运行方式的契合,正是Go语言的核心优势所在。

笔者结合自己的理解和使用 Go 语言进行软件开发的经验,并根据重要性将 Go 语言的特点进行了整理,具体如下:

1、“少即是多”的设计哲学

Go 语言的设计哲学是“少即是多”,它没有太多的创新,语法糖也少,只有 25 个关键字,但是它却支持面向接口编程、并发、垃圾回收、内存自动管理等功能,同时它还像 C语言一样简洁,且支持 C语言函数的调用。

Go 语言的“少”使得开发者可以快速上手,且开发效率很高。Go 语言的代码简洁易读,编译速度也很快。

下面的代码实现了调用 API 查询天气预报并返回最高和最低温度的功能:
type Weather struct{}
 
//调用 API 查询天气预报并返回最高和最低温度
func (w *Weather) GetTemperature(cityId string) (float64, float64, error) {
    var url string
    resp, err := http.Get(url)
    if err != nil {
  return 0, 0, err
    }
    defer resp.Body.Close()
    var data struct {
        Main struct {
                        city        string    `json:"city"`
                        tempHigh    float64   `json:"tem1"`
                        tempLow     float64   `json:"tem2"`
                        updateTime  time.Time `json:"update_time"`
        } `json:"main"`
    }
    if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
         return 0, 0, err
    }
 
    return data.Main.tempHigh, data.Main.tempLow, nil
}
这段代码虽行数不多,但清晰地展示了 Go语言的各种特性。它使用了结构体、方法、多返回值函数、defer 关键字、指针和标准库,且实现了简单声明变量、错误处理和序列化等功能。

2、强大的runtime

Go 语言具有强大的 runtime,支持语言级别的并发、高效的内存管理和自动垃圾回收机制。

1) 语言级别的并发

一些编程语言需要通过包装库来实现并发,这可能会降低代码的可读性。然而,Go 在语言层面就实现了并发,Go 语言的并发特性体现在能够充分利用多核提高 CPU 的并发利用率上,并且从单核转换为多核的成本也较低。

编写并发代码的过程很简单,即使是刚接触 Go语言的开发者也能轻松编写出高并发程序,这也是众多开发人员选择 Go语言的原因之一。

下面的代码用于开启 1000 个协程(goroutine),且并发输出传入的 i 值:
func Print(i int) {
    fmt.Println("goroutine:", i)
}
 
func main() {
    for i := 0; i < 1000; i++ {
            go Print(i)
    }
    time.Sleep(time.Second)
}
从上述代码可知,使用 Go语言进行并发编程非常简单,仅仅使用一个关键字 go 即可。其中涉及的 CPU 绑定、时间切片优化等工作,已由 Go语言底层进行了封装。另外,Go 并发源码的可读性非常强。

Go 语言能实现高并发得益于其实现的 GPM 调度模型。G、P、M 这三个字母分别指模型中的协程、逻辑处理器和工作线程,Go 语言在用户层实现了 GPM 调度模型。

Go 语言中的协程是轻量级的线程,每个协程占用约 2KB 的内存,因此可以有效利用多核进行大规模的并发处理。在 Go语言中实现并发的原因很简单,就是为了高效地完成协程对 CPU 资源的调度。因此,Go语言调度的核心是根据特定的算法将协程投放到操作系统的线程中执行。因为 Go 程序在 runtime 中就实现了自己的调度器,所以我们常说 Go在语言级别支持并发性。

在高并发场景中,可能会出现临界资源安全(数据冲突)问题。为了解决这个问题,除了使用传统的锁,Go语言还实现了极具特色的通信顺序进程(Communicating Sequential Processes,CSP)模型。该模型吸收了管道通信机制的优点,形成了独特的通道(channel)数据类型。

2) 高效的内存管理

Go 语言的内存管理借鉴了 Google 公司为 C语言开发的 TCMalloc 算法。

TCMalloc 算法的核心思想是将内存分级管理,从而降低锁的粒度,还可以通过分配连续的内存来减少内存碎片。它可以替代系统中与内存分配相关的函数(如malloc、free、new等)。

Go语言借鉴这个思路,将内存按照大小分配给不同的对象,并通过一些高效的内存分配算法来实现内存管理。内存分配、管理的大致过程如下:
内存使用多级分配的管理方式,每个线程维护自己的本地内存池。进行内存分配时,优先从本地内存池中分配,这种方式不需要加锁,可以避免或减少因频繁向全局内存池申请内存而引发的竞争。当本地内存池中的内存不足时,再向上一级内存池申请。

3) 自动垃圾回收机制

C语言虽然性能好,但对开发人员的要求也很高,因为开发人员需要负责管理内存(包括分配和释放内存等),稍不注意就可能导致内存泄漏等问题。

在 Go语言中,栈和堆是 Go程序在运行过程中分配和管理内存的两个主要区域,它们有各自的特点且承担着不同的责任:
当然,自动进行垃圾回收的语言其性能赶不上手动管理内存的语言。

自动垃圾回收机制的好坏取决于 STW 的长短。Go 语言使用三色标记、混合写屏障、并发增量回收等机制来提高垃圾回收的性能。在 Go1.14 版本中,垃圾回收的时间已经达到了亚毫秒级。

3、面向接口编程

在 Go语言中,接口扮演着重要的角色。

接口提供了一种抽象机制,使得程序员可以编写更灵活、可扩展的代码。通过接口,不同的类型可以在共享相同行为(方法)的情况下实现松耦合的交互,从而提高代码的可维护性和可重用性。

Go 语言中没有类的概念,因此它将面向对象的三个基本特征(封装、继承和多态)以自己的方式实现。Go语言通过接口实现了鸭子类型(Duck Typing),这是一种动态类型的编程范式。鸭子类型基于一个简单的原则实现:如果一个对象走起来像鸭子,叫起来像鸭子,那么它就可以被认为是鸭子。换句话说,鸭子类型关注的是对象的行为,而不是对象的实际类型。

在 Go语言中,接口是一个或多个方法签名的集合,任何实现了这些方法的类型都可以被认为实现了该接口。这是一种隐式的实现,没有显式地使用 implement 之类的关键字。

需要注意的是,尽管 Go语言是静态类型的,但因为接口实现了鸭子类型,所以它的类型处理更为灵活。Go语言官方建议使用组合方式实现继承这个特征。一般情况下,组合是“has-a”关系,继承是“is-a”关系,相比之下,组合的耦合更松。

在笔者看来,Go 语言就是一门基于连接与组合实现的编程语言。在 Go语言中,连接指的是对各个函数或方法进行串联的方式,组合指的是将简单对象组合成复杂对象的方式。编程的目标就是化繁为简,充分运用组合的思想大幅简化了开发模式和项目代码,实现返璞归真。

4、为工程服务的语言

从 Go语言的“出身”来看,它更像学院派语言,但实际上设计它的初衷是将其用作为工程服务的语言。

Go 语言具有开发效率高、代码规范统一、写并发相对容易等特点,非常适合团队开发。它的应用场景包括基础架构、中间件、云原生开发等。

Go语言简化了指针设计,实现了将变量初始化为零值、通过 runtime 分配内存、内存逃逸分析以及自动垃圾回收等功能,让内存使用变得更为简单和安全。从这些实现细节中,我们可以深刻地认识到 Go语言是一门为工程服务的编程语言。

1) 静态强类型编译型语言

Go 语言是一种静态强类型编译型语言,它具有以下特征:

2) 静态链接

关于静态链接和动态链接的争议一直存在。在笔者看来,在存储已经很便宜的今天,静态链接可能是更好的选择。Go语言正好属于静态链接库阵营。

在 Go语言中,一个几千字节大小的“hello world”程序编译后的可执行文件会达到几兆字节大小,这是因为 Go语言在编译时会将一些静态链接库以及 runtime 环境打包到可执行文件中,虽然体积变大了,但程序更具可移植性和独立性了。不要低估了这一点,因为在复杂的生产系统中,使用动态链接可能会存在程序依赖于多个版本库的问题。

此外,静态链接还具有另一个优势,即编译一次即可在任何地方运行。众所周知,部署 Java 程序需要在运行环境中安装 JRE,而编译后的 Go语言可执行文件并不依赖于任何运行环境,也就是说,它可以直接运行。这进一步证明了 Go语言是以实用为主的工程语言。

3) 编译速度快

“天下武功唯快不破”,程序的编译与执行也是如此。但是,对于快速编译和快速执行,我们往往只能选择其一,就像鱼与熊掌不可兼得一样。

C语言运行速度快,但编译速度慢,依赖库运行时错误很多。Java 的执行速度和编译速度都不错,但在不同的环境下,需要使用不同的 JVM 运行代码。

Go 语言一开始就考虑实现快速编译,就像解释型语言一样,我们不会注意到它正在编译。用Go语言编写的项目可以在秒级完成编译,这对提高生产力很重要!

4) 执行性能高

Go语言虽然简单,语法糖少,但性能不差。它的性能略低于 C++,相当于 Java,比 Python 快几十倍。

下表是 The Benchmarks Game 中列出的编程语言性能数据,其中给出了在 10 种性能测试方法下多种语言的运行时间。

表:10种性能测试方法下多种语言的运行时间对比(单位:秒)
性能测试方法 Go Java C++ Python
fannkuch-redux 8.28 11.00 8.08 367.49
pidigits 0.6 0.9 0.6 1.28
fasta 1.20 1.20 0.78 39.10
n-body 6.38 6.75 4.09 586.17
spectral-norm 1.43 1.68 0.72 118.4
reverse-complement 1.43 1.5 0.63 7.16
redux 3.61 5.7 1.8 1.36
k-nucleotide 8.29 5.00 1.95 6.37
mandelbrot 3.75 4.11 0.4 172.5
binary-trees 6.7 2.50 1.04 49.3

从表中可以看出,在这 10 种性能测试方法下,C++ 的性能最好,Go 与 Java 差不多,Python 要慢几个数量级。

5) 支持跨平台交叉编译

Go 语言支持跨平台交叉编译,可以轻松地根据指定的平台编译源码并运行。

6) 拥有丰富的工具链

Go语言拥有丰富的工具链(命令行工具),包括编译、运行调试、下载和代码格式化等工具。

5、自带标准化的测试框架

Go 语言自带标准化的测试框架,可以很好地执行单元、黑盒、白盒、压力和模糊(模糊测试是 Go1.18 版本中新增的功能)等多种测试。

除了标准化的测试框架,它还有很多第三方优秀的测试框架。

建议一个项目仅使用一套测试框架,以确保项目的完整性和一致性。

6、丰富的标准库和第三方库

Go语言拥有非常丰富的标准库和第三方库,这些库几乎涵盖了所有常用的功能和操作,涉及网络编程、数据处理、文件操作、并发处理、加密算法、图片处理、数据库操作等。

借助这些库,我们可以轻松地编写出高可用、高性能且具有良好可维护性的代码!

更进一步说,Go 语言的活跃社区和开源生态系统使得开发者可以快速找到合适的库来满足特定的需求。Go 语言简洁的语法和清晰的代码组织结构也使得第三方库易于理解和集成。以上这些因素都有助于提高我们的开发效率,这进一步体现了 Go 语言在工程实践中的优势。

Go语言强大的生态圈和成功案例

最初 Go语言的定位是成为系统编程语言,但由于自动垃圾回收机制会影响性能,所以在系统层面人们更多还是会选择 C和 C++语言。

如今,在大数据和电商领域,Java 占据主导地位;在人工智能领域,Python 占据主导地位。Go 语言则常被选择用于构建位于上层应用和底层操作系统之间的中间层。

在云原生领域,大多数项目是使用 Go语言实现的,包括著名的容器运行时代表 Docker、事实上的容器编排标准 Kubernetes、分布式 KV 存储系统 Etcd、监视的首选 Prometheus 等。目前,一些大型 IT 公司也在使用 Go语言重构部分业务。可以说,Go 语言是云原生的第一开发语言。

Go 语言赶上了云时代,促进了云的发展,云原生和 Go语言互相成就。

相关文章