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,这些都是保障代码能正确而稳定运行的必备利器。
除此之外,还可通过环境变量输出运行时监控信息,尤其是垃圾回收和并发调度跟踪,可进一步帮助我们改进算法,获得更佳的运行期表现。
- Golang语言之旅
- 第一章:初始小节以及安装
- 一:Golang语言特性
- 二:Windows上安装Go语言开发包
- 三:在Mac OS上安装Go语言开发包
- 第二章:GO语言注意事项
- 一:Dos的常用指令
- 第三章:Go初识小菜
- 一:Go语言之变量与常量
- 二:Go内置值-引用类型
- 三:基本的数据类型
- 四:字符串(char)
- 五:布尔类型(bool)
- 六:字符串类型(string)
- 七:基本数据类型的默认值
- 八:基本数据类型的互相转换
- 九:基本数据类型和string类型的相互转换
- 十:Golang指针
- 十一:值类型和引用类型
- 十二:标识符和命名规范
- 十三:系统保留关键字and预定义标识符
- 十四:fmt常用方法解析
- 第四章:Go运算符
- 一:运算符的基本介绍
- 二:算术运算符
- 2.1:算数运算符细节
- 三:关系运算符
- 3.1:关系运算符细节
- 四:逻辑运算符
- 4.1:逻辑运算符细节及案例
- 五:Go赋值运算符
- 5.1:案例演示赋值运算符的基本使用
- 5.2:赋值运算符的特点
- 六:Go位运算符
- 七:其他运算符
- 八:运算符的优先级
- 九:控制台输入语句
- 十:进制
- 十一:位运算
- 第五章:流程控制大纲
- 一:if语句
- 二:switch语句
- 三:for循环
- 第六章:函数-包-错误处理
- 一:Go函数
- 二:Go包
- 三:匿名函数
- 四:闭包
- 五:函数defer
- 六:函数参数的传递方式
- 七:变量的作用域
- 八:时间和日期相关函数
- 九:new和recover异常
- 十:数组(Array)切片(Section)
- 十一:切片(slice)
- 十二:3 数组的排序和查找
- 第七章:Map
- 第一节:Map基础认识
- 第二节:Map初始化和make
- 第三节:Map增删改查
- 第四节:Map的切片
- 第五节:Map的注意事项
- 第八章:面向对象(上)
- 第一节:结构体(值类型)
- 第二节:方法
- 第三节:面向对象编程应用实例
- 第九章:面向对象(下)
- 第一节:面向对象之抽象
- 第二节:面向对象之继承
- 第三节:面向对象之多态
- 第四节:接口
- 第十章:文件操作
- 第一节:文件基本介绍
- 第二季:写文件实例操作
- 第三节:JSON
- 第十一章:单元测试
- 第一节:单元测试介绍
- 第二节:单元测试案例
- 第三节:单元测试总结
- 第四节:单元测试案例
- 第十二章:goroutine和channel
- 第一节:goroutine基本介绍
- 第二节:goroutine入门案例
- 第三节:goroutione调度模型
- 第四节:Golang设置运行的CPU数量
- 第十二章:channel
- 第一节:channel基本介绍
- 第二节:channel基本使用
- 第三节:channel案例演示
- 第四节:channel 使用的注意事项
- 第五节:channel练习题
- 第六节:channel的遍历和关闭
- 第七节:goroutione和channel结合
- 第八节:channel细节处理
- 第十二章:并发模式
- 第十三章:反射reflect
- 第一节:反射基本介绍
- 第二节:反射重要的函数和概念
- 第三节:反射快速入门案例
- 第四节:反射注意事项
- 第五节:反射练习题
- 第六节:反射最佳实践