Golang-语言简介1

http://c.biancheng.net/golang/intro/

Go语言的特性

Go语言也称为 Golang,是由 Google 公司开发的一种静态强类型、编译型、并发型、并具有垃圾回收功能的编程语言。

接下来从几个方面来具体介绍一下Go语言的特性。

语法简单

抛开语法样式不谈,单就类型和规则而言,Go 与 C99、C11 相似之处颇多,这也是Go语言被冠以“NextC”名号的重要原因。

Go语言的语法处于简单和复杂的两极。C语言简单到你每写下一行代码,都能在脑中想象出编译后的模样,指令如何执行,内存如何分配,等等。而 C 的复杂在于,它有太多隐晦而不着边际的规则,着实让人头疼。相比较而言,Go 从零开始,没有历史包袱,在汲取众多经验教训后,可从头规划一个规则严谨、条理简单的世界。

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

将“++”、“--”从运算符降级为语句,保留指针,但默认阻止指针运算,带来的好处是显而易见的。还有,将切片和字典作为内置类型,从运行时的层面进行优化,这也算是一种“简单”。

并发模型

时至今日,并发编程已成为程序员的基本技能,在各个技术社区都能看到诸多与之相关的讨论主题。在这种情况下Go语言却一反常态做了件极大胆的事,从根本上将一切都并发化,运行时用 Goroutine 运行所有的一切,包括 main.main 入口函数。

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

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

内存分配

将一切并发化固然是好,但带来的问题同样很多。如何实现高并发下的内存分配和管理就是个难题。好在 Go 选择了 tcmalloc,它本就是为并发而设计的高性能内存分配组件。

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

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

垃圾回收

垃圾回收一直是个难题。早年间,Java 就因垃圾回收低效被嘲笑了许久,后来 Sun 连续收纳了好多人和技术才发展到今天。可即便如此,在 Hadoop 等大内存应用场景下,垃圾回收依旧捉襟见肘、步履维艰。

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

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

静态链接

Go 刚发布时,静态链接被当作优点宣传。只须编译后的一个可执行文件,无须附加任何东西就能部署。这似乎很不错,只是后来风气变了。连着几个版本,编译器都在完善动态库 buildmode 功能,场面一时变得有些尴尬。

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

标准库

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

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

当然,优秀第三方资源也是语言生态圈的重要组成部分。近年来崛起的几门语言中,Go 算是独树一帜,大批优秀作品频繁涌现,这也给我们学习 Go 提供了很好的参照。

工具链

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

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

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

在早期 CPU 都是以单核的形式顺序执行机器指令。Go语言的祖先C语言正是这种顺序编程语言的代表。顺序编程语言中的顺序是指:所有的指令都是以串行的方式执行,在相同的时刻有且仅有一个 CPU 在顺序执行程序的指令。

随着处理器技术的发展,单核时代以提升处理器频率来提高运行效率的方式遇到了瓶颈,单核 CPU 发展的停滞,给多核 CPU 的发展带来了机遇。相应地,编程语言也开始逐步向并行化的方向发展。

虽然一些编程语言的框架在不断地提高多核资源使用效率,例如 Java 的 Netty 等,但仍然需要开发人员花费大量的时间和精力搞懂这些框架的运行原理后才能熟练掌握。

作为程序员,要开发出能充分利用硬件资源的应用程序是一件很难的事情。现代计算机都拥有多个核,但是大部分编程语言都没有有效的工具让程序可以轻易利用这些资源。编程时需要写大量的线程同步代码来利用多个核,很容易导致错误。

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

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

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

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

提示:Go语言通过通道可以实现多个 goroutine 之间内存共享。

【实例】生产者每秒生成一个字符串,并通过通道传给消费者,生产者使用两个 goroutine 并发运行,消费者在 main() 函数的 goroutine 中进行处理。

  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "time"
  6. )
  7. // 数据生产者
  8. func producer(header string, channel chan<- string) {
  9. // 无限循环, 不停地生产数据
  10. for {
  11. // 将随机数和字符串格式化为字符串发送给通道
  12. channel <- fmt.Sprintf("%s: %v", header, rand.Int31())
  13. // 等待1秒
  14. time.Sleep(time.Second)
  15. }
  16. }
  17. // 数据消费者
  18. func customer(channel <-chan string) {
  19. // 不停地获取数据
  20. for {
  21. // 从通道中取出数据, 此处会阻塞直到信道中返回数据
  22. message := <-channel
  23. // 打印数据
  24. fmt.Println(message)
  25. }
  26. }
  27. func main() {
  28. // 创建一个字符串类型的通道
  29. channel := make(chan string)
  30. // 创建producer()函数的并发goroutine
  31. go producer("cat", channel)
  32. go producer("dog", channel)
  33. // 数据消费函数
  34. customer(channel)
  35. }

Go语言的性能

根据 Go 开发团队和基本的算法测试,Go语言与C语言的性能差距大概在 10%~20% 之间。虽然没有官方的性能标准,但是与其它各个语言相比已经拥有非常出色的表现。

时下流行的语言大都是运行在虚拟机上,如:Java 和 Scala 使用的 JVM,C# 和 VB.NET 使用的 .NET CLR。尽管虚拟机的性能已经有了很大的提升,但任何使用 JIT 编译器和脚本语言解释器的编程语言(Ruby、Python、Perl 和 JavaScript)在 C 和 C++ 的绝对优势下甚至都无法在性能上望其项背。

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

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

常见编程语言的运行性能比较
编程语言↓ / 测试用例→reverse-complementpidigitsfannkuch-reduxfastaspectral-normn-bodyk-nucleotidemandelbrotbinary-treesregex-redux
C语言 0.42 1.73 8.97 1.33 1.99 9.96 5.38 1.65 2.38 1.45
C++ 0.6 1.89 10.35 1.48 1.99 9.31 7.18 1.73 2.36 17.14
Go 0.49 2.02 14.49 2.17 3.96 21.47 14.79 5.46 35.18 29.29
Java 1.13 3.12 15.09 2.32 4.25 22.56 8.38 6.08 8.58 10.38
JavaScript 4.3 N/A 81.49 9.79 16.17 28.74 66.07 19.04 53.64 4.44


通过上表可以看出,Go语言在性能上更接近于 Java 语言,虽然在某些测试用例上不如经过多年优化的 Java 语言,但毕竟 Java 语言已经经历了多年的积累和优化。Go语言在未来的版本中会通过不断的版本优化提高单核运行性能。

Go语言标准库强大

在Go语言的安装文件里包含了一些可以直接使用的包,即标准库。Go语言的标准库(通常被称为语言自带的电池),提供了清晰的构建模块和公共接口,包含 I/O 操作、文本处理、图像、密码学、网络和分布式应用程序等,并支持许多标准化的文件格式和编解码协议。

在 Windows 下,标准库的位置在Go语言根目录下的子目录 pkg\windows_amd64 中;在 Linux 下,标准库在Go语言根目录下的子目录 pkg\linux_amd64 中(如果是安装的是 32 位,则在 linux_386 目录中)。一般情况下,标准包会存放在 $GOROOT/pkg/$GOOS_$GOARCH/ 目录下。

Go语言的编译器也是标准库的一部分,通过词法器扫描源码,使用语法树获得源码逻辑分支等。Go语言的周边工具也是建立在这些标准库上。在标准库上可以完成几乎大部分的需求。

Go语言的标准库以包的方式提供支持,下表列出了Go语言标准库中常见的包及其功能。

Go语言标准库常用的包及功能
Go语言标准库包名功  能
bufio 带缓冲的 I/O 操作
bytes 实现字节操作
container 封装堆、列表和环形列表等容器
crypto 加密算法
database 数据库驱动和接口
debug 各种调试文件格式访问及调试功能
encoding 常见算法如 JSON、XML、Base64 等
flag 命令行解析
fmt 格式化操作
go Go语言的词法、语法树、类型等。可通过这个包进行代码信息提取和修改
html HTML 转义及模板系统
image 常见图形格式的访问及生成
io 实现 I/O 原始访问接口及访问封装
math 数学库
net 网络库,支持 Socket、HTTP、邮件、RPC、SMTP 等
os 操作系统平台不依赖平台操作封装
path 兼容各操作系统的路径操作实用函数
plugin Go 1.7 加入的插件系统。支持将代码编译为插件,按需加载
reflect 语言反射支持。可以动态获得代码中的类型信息,获取和修改变量的值
regexp 正则表达式封装
runtime 运行时接口
sort 排序接口
strings 字符串转换、解析及实用函数
time 时间接口
text 文本模板及 Token 词法器


当然,优秀第三方资源也是语言生态圈的重要组成部分。近年来崛起的几门语言中,Go 算是独树一帜,大批优秀作品频繁涌现,这也给我们学习 Go 提供了很好的参照。

在Mac OS上安装Go语言开发包

本节主要为大家讲解如何在Mac OS上安装Go语言开发包,大家可以在Go语言官网下载对应版本的的安装包(https://golang.google.cn/dl/),如下图所示。

下载Mac OS版Go语言开发包

安装Go语言开发包

Mac OS 的Go语言开发包是 .pkg 格式的,双击我们下载的安装包即可开始安装。


Mac OS 下是傻瓜式安装,一路点击“继续”即可,不再赘述。


安装包会默认安装在 /usr/local 目录下,如下所示。

Go 的安装目录


安装完成之后,在终端运行 go version,如果显示类似下面的信息,表明安装成功。

go version go1.13.4 darwin/amd64

设置 GOPATH 环境变量

开始写 go 项目代码之前,需要我们先配置好环境变量。编辑 ~/.bash_profile(在终端中运行 vi ~/.bash_profile 即可)来添加下面这行代码(如果你找不到 .bash_profile,那自己创建一个就可以了)

export GOPATH=$HOME/go

保存然后退出你的编辑器。然后在终端中运行下面命令

source ~/.bash_profile

提示:$HOME 是每个电脑下的用户主目录,每个电脑可能不同,可以在终端运行 echo $HOME 获取

GOROOT 也就是 Go 开发包的安装目录默认是在 /usr/local/go,如果没有,可以在 bash_profile 文件中设置。

export GOROOT=/usr/local/go

然后保存并退出编辑器,运行 source ~/.bash_profile 命令即可。

Goland 2019下载和安装(带破解补丁和汉化包)

工欲善其事必先利其器,命令行工具虽然能够满足我们一些基本操作的需求,但实际工作中怎么能没有一款趁手的开发工具呢?

学习Go语言同样如此,一个强大的集成开发环境(IDE)能够大大提高我们的开发效率,目前市面上比较流行的有下面这几种:

  • GoLand:由 JetBrains 公司开发的一个新的商业 IDE;
  • LiteIDE X:一款简单、开源、跨平台的Go语言 IDE;
  • 第三方插件:Sublime Text、VS Code 等编辑器都有Go语言的相关插件。


本节我们主要介绍 Windows 系统上 Goland 的安装和配置过程。

Goland 简介

Goland 是由 JetBrains 公司开发的一个新的商业 IDE,旨在为 Go 开发者提供的一个符合人体工程学的新的商业 IDE。Goland 整合了 IntelliJ 平台(一个用于 java 语言开发的集成环境,也可用于其他开发语言),提供了针对Go语言的编码辅助和工具集成。

Goland 下载和安装

大家可以从 Goland 的官网下载对应版本的 Goland 的安装包:(教程中使用的版本是 2019.2.3)

 

posted @ 2022-04-07 10:57  hanease  阅读(162)  评论(0编辑  收藏  举报