Go 语言编程 — Overview

目录

缘起

Golang 始于 2007 年,2009 年 11 月正式开源,2012 发布了 Go 1 稳定版本。

创始人:

  • Ken Thompson(肯·汤普森):贝尔实验室 Unix 团队成员,C 语言、Unix 和 Plan 9 的创始人之一。
  • Rob Pike:Golang 项目总负责人,贝尔实验室 Unix 团队成员,合著了《The Unix Programming Environment》,对 UNIX 的设计理念做了正统的阐述。
  • Robert Griesemer:就职于 Google,负责 Chrome 浏览器和 Node.js 使用的 Google V8 JavaScript 引擎的代码生成部分,对语言设计有深入的认识。

后来还加入了 Ian Lance Taylor、Russ Cox 等人。这些计算机科学领城的重量级人物设计 Golang 的初衷是满足 Google 的需求:在不损失应用程序性能的情况下降低代码的复杂性,具有 “部署简单、并发性好、语言设计良好、执行性能好” 等优势。

计算机软件经历了数十年的发展,形成了多种学术流派,例如:面向过程编程、面向对象编程、函数式编程、面向消息编程等。此外,近年来也出现了一些小众的编程哲学。

Golang 对这些思想亦有所吸收。例如:Golang 接受了函数式编程的一些想法,支持匿名函数与闭包;Golang 接受了以 Erlang 语言为代表的面向消息编程思想,支持 Goroutine 和 Channel,并推荐使用消息而不是共享内存来进行并发编程。

在 Golang 出现之前,开发者们总是面临非常艰难的抉择,究竟是使用执行速度快但是编译速度并不理想的语言(如:C++),还是使用编译速度较快但执行效率不佳的语言(如:.NET、Java),或者说开发难度较低但执行速度一般的动态语言(如:Python)呢?

在 Google I/O 2012 的 Go 设计小组见面会上,Rob Pike 是这样说的:“我们做了大量的 C++ 开发,厌烦了等待编译完成,尽管这是玩笑,但在很大程度上来说也是事实。”

显然,Go 语言在这 3 个条件之间做到了最佳的平衡:快速编译,高效执行,易于开发

Golang 的吉祥物

Go Gopher,这是才华横溢的插画家 Renee French 设计的,她也是 Go 设计者之一 Rob Pike 的妻子。
在这里插入图片描述

Golang 的特性

语法简单

Golang 的语法规则严谨,没有歧义,更没什么黑魔法变异用法。任何人写出的代码都基本一致,这使得 Golang 简单易学。放弃部分 “灵活” 和 “自由”,换来更好的维护性。

将 ++ 、-- 从运算符降级为语句,虽然保留了指针,但默认阻止指针运算。将切片和字典作为内置类型,从运行时的层面进行优化,这也算是一种 “简单”。

原生支持并发编程

Golang 是在多核和网络化的时代背景下诞生的原生支持并发的编程语言。Golang 从底层原生支持并发,无须第三方库,开发人员可以很轻松地在编写程序时决定怎么使用 CPU 资源。

Golang 的并发基于 Goroutine,Goroutine 类似于线程,但并非线程。可以将 Goroutine 理解为一种虚拟线程。Golang 运行时会参与调度 Goroutine,并将 Goroutine 合理地分配到每个 CPU 中,最大限度地使用 CPU 性能。

可以说,Goroutine 是 Go 最显著的特征。它用 “类协程” 的方式来处理并发单元,却又在运行时层面做了更深度的优化处理。这使得语法上的并发编程变得极为容易,无须处理回调,无须关注线程切换,仅需使用一个 go 关键字,简单而自然。

多个 Goroutine 中,Golang 使用 Channel(通道)进行通信,通道是一种内置的数据结构,可以让用户在不同的 Goroutine 之间同步发送具有类型的消息。这让编程模型更倾向于在 Goroutine 之间发送消息,而不是让多个 Goroutine 争夺同一个数据的使用权。

Goroutine 搭配 Channel,实现 CSP 模型。将并发单元间的数据耦合拆解开来,各司其职,这对所有纠结于内存共享、锁粒度的开发人员都是一个可期盼的解脱。若说有所不足,那就是应该有个更大的计划,将通信从进程内拓展到进程外,实现真正意义上的分布式。

程序可以将需要并发的环节设计为生产者模式和消费者的模式,将数据放入通道。通道另外一端的代码将这些数据进行并发计算并返回结果,如下图所示:

在这里插入图片描述

内存分配

Go 选择了 tcmalloc 内存分配器,它本就是为并发而设计的高性能内存分配组件。

可以说,内存分配器是运行时三大组件里变化最少的部分。刨去因配合垃圾回收器而修改的内容,内存分配器完整保留了 tcmalloc 的原始架构。使用 cache 为当前执行线程提供无锁分配,多个 central 在不同线程间平衡内存单元复用。在更高层次里,heap 则管理着大块内存,用以切分成不同等级的复用内存块。快速分配和二级内存平衡机制,让内存分配器能优秀地完成高压力下的内存管理任务。

在最近几个版本中,编译器优化卓有成效。它会竭力将对象分配在栈上,以降低垃圾回收压力,减少管理消耗,提升执行性能。可以说,除偶尔因性能问题而被迫采用对象池和自主内存管理外,我们基本无须参与内存管理操作。

自动垃圾回收

在垃圾回收方面,Go 面临了很多困难。因指针的存在,所以回收内存不能做收缩处理。幸好,指针运算被阻止,否则要做到精确回收都难。

每次升级,垃圾回收器必然是核心组件里修改最多的部分。从并发清理,到降低 STW 时间,直到 Go 的 1.5 版本实现并发标记,逐步引入三色标记和写屏障等等,都是为了能让垃圾回收在不影响用户逻辑的情况下更好地工作。尽管有了努力,当前版本的垃圾回收算法也只能说堪用,离好用尚有不少距离。

使用静态链接

静态编译的好处显而易见。将运行时、依赖库直接打包到可执行文件内部,简化了部署和发布操作,无须事先安装运行环境和下载诸多第三方库。这种简单方式对于编写系统软件有着极大好处,因为库依赖一直都是个麻烦。

支持交叉编译

Golang 支持交叉编译,可以在 Linux 操作系统上开发运行于 Windows 的应用程序。

国际化

这是第一门完全支持 UTF-8 的编程语言,这不仅体现在它可以处理使用 UTF-8 编码的字符串,就连它的源码文件格式都是使用的 UTF-8 编码。Go 语言做到了真正的国际化!

标准库

功能完善、质量可靠的标准库为编程语言提供了充足动力。在不借助第三方扩展的情况下,就可完成大部分基础功能开发,这大大降低了学习和使用成本。最关键的是,标准库有升级和修复保障,还能从运行时获得深层次优化的便利,这是第三方库所不具备的。

Go 标准库虽称不得完全覆盖,但也算极为丰富。其中值得称道的是 net/http,仅须简单几条语句就能实现一个高性能 Web Server,这从来都是宣传的亮点。更何况大批基于此的优秀第三方 Framework 更是将 Go 推到 Web/Microservice 开发标准之一的位置。

当然,优秀第三方资源也是语言生态圈的重要组成部分。

工具链

完整的工具链对于日常开发极为重要。Go 在此做得相当不错,无论是编译、格式化、错误检查、帮助文档,还是第三方包下载、更新都有对应的工具。其功能未必完善,但起码算得上简单易用。

内置完整测试框架,其中包括单元测试、性能测试、代码覆盖率、数据竞争,以及用来调优的 pprof,这些都是保障代码能正确而稳定运行的必备利器。

除此之外,还可通过环境变量输出运行时监控信息,尤其是垃圾回收和并发调度跟踪,可进一步帮助我们改进算法,获得更佳的运行期表现。

Golang 的性能

根据 Go 开发团队和基本的算法测试,Golang 与 C 语言的性能差距大概在 10%~20% 之间。

这里以国外的一个编程语言性能测试网站 http://benchmarksgame.alioth.debian.org/ 为测试基准和数据源。这个网站可以对常见的编程语言进行性能比较,网站使用都是最新的语言版本和常见的一些算法。

通过对 C(GCC)、C++、Java、JavaScript 和 Golang 的测试。性能比较如下表所示,表中数据的单位为秒,数值越小表明运行性能越好。

在这里插入图片描述

哪些项目使用 Golang 开发?

编程语言:

  • Golang:https://github.com/golang/go
  • delve:Golang 强大的调试器,被很多集成环境和编辑器整合。https://github.com/derekparker/delve

云原生:

  • Docker:https://github.com/docker/docker
  • Kubernetes:https://github.com/kubernetes/kubernetes
  • Etcd:https://github.com/coreos/etcd

Web Server:

  • Beego:是一个类似 Python 的 Tornado 框架,采用了 RESTFul 的设计思路,使用Go语言编写的一个极轻量级、高可伸缩性和高性能的 Web 应用框架。https://github.com/astaxie/beego

猜你喜欢

转载自blog.csdn.net/Jmilk/article/details/107130060