Go语言基础入门
Go语言的logo
为什么需要一个新的语言
最近十年来,C/C++在计算机领域得到了很好的发展,并没有新的系统编程语言出现。对开发程度和执行效率在很多情况下并不能兼得。要么是执行效率高,但是低效的开发和编译,如C++;要么是执行效率低,但是高效的开发和编译,如.NET,Java;所以需要一种新的语言,拥有比较高效的执行和开发编译,所以也就有了Go语言
传统的语言如C++,大家需要花费较多的时间学习如何使用这门语言,而不是如何更好地表达写程序人的思想,同时编译花费的时间太长,对于编写——》编译——》运行这个链条来说周期太长。动态语言如Python,由于没有强类型的约束,很多问题需要在运行时才能发现。这种低级错误更应该交给编译器来处理。
学习Go语言可以做什么
鉴于Go语言的简单,高效,并发特性,Go语言作为服务器编程语言,很适合处理日志、数据打包、虚拟机处理、文件系统、分布式系统、数据库代理等;网络编程方面,Go语言广泛用于Web应用、API应用、下载应用等;除此之外,Go语言还适用于内存数据库和云平台领域。
Go语言的语法清爽
Go语言语法类似于C语言,因此熟悉C语言以及其派生语言C++、C#等的人都会迅速熟悉这门语言。
C语言的有些语法会让代码的可读性降低甚至发生歧义。Go语言在C语言的基础上取其精华去其糟粕,将C语言中较为容易发生错误的写法进行调整,做出相应的编译提示。
去掉循环冗余的括号
// C语言的for循环
for(int a = 0; a < 10; a++) {
// 循环代码
}
Go语言中这种循环变为:
for a := 0; a < 10; a++ {
// 循环代码
}
for 两边的括号被去掉, int声明被简化为:=,直接通过编译器右值推导获得a的变量类型并声明。
去掉表达式冗余括号
if (表达式) {
//表达式成立
}
Go语言简化后:
if 表达式 {
//表达式成立
}
强制的代码风格
Go语言中,左括号必须紧接着语句不换行。其他样式的括号将被视为代码编译错误。这个特性刚开始会使开发者有一些不习惯,但随着对Go语言的不断熟悉,开发者就会发现风格统一让大家在阅读代码时吧注意力集中到了解决问题上,而不是代码风格上面。
同时Go语言也提供了一套格式化工具,一些Go语言的开发环境或者编译器在保存时,都会使用格式化工具对代码进行格式化,让代码提交的时候是统一格式的代码。
不再纠结于i++还是++i
C语言非常经典的考题为:
int a, b;
a = i++;
b = ++i;
这种题目对于初学者简直摸不到头脑,为什么一个简单的自增表达式需要有两种写法?
在Go语言中,自增操作符不再是一个操作符,而是一个语句。因此,在Go语言中自增只有一种写法:
i++
如果写成前置自增++i,或者赋值后自增a = i++都将导致编译错误。
Go语言的特性
Go语言也称为Golang,是由Google公司开发的一种静态强类型、编译型、并发型、并具有垃圾回收功能的编程语言。
接下来从几个方面来具体介绍Go语言的特性。
语法简单
抛开语法样式不谈,单就类型和规则而言,Go与C99, C11,相似之处颇多,这也是Go语言被冠以“Next C”名号的重要原因。
Go语言的语法处于简单和复杂的两极。C语言简单到你每写下一行代码,都能在脑中想象出编译后的模样,指令如何执行,内存如何分配,等等。而C的复杂在于,它有太多隐而不着边际的规则,着实让人头疼。相比较而言,Go 从零开始,没有历史包袱,在汲取众多经验教训后,可从头规划一个规则严谨、条理简单的世界。
Go语言的语法规则严谨,没有岐义,更没什么黑魔法变异用法。任何人写出的代码都基本一致,这使得Go语言简单易学。放弃部分"灵活”和“自由”,换来更好的维护性,我觉得是值得的
将“++"、“–"从运算符降级为语句,保留指针,但默认阻止指针运算,带来的好处是显而易见的。还有,将切片和字典作为内置类型,从运行时的层面进行优化,这也算是一种”简单”。
并发模型
时至今日,并发编程已成为程序员的基本技能,在各个技术社区都能看到诸多与之相关的讨论主题。在这种情况下Go语言却一反常态做了件极大胆的事,从根本上将一切都并发化,运行时用 Goroutine 运行所有的一切,包括 main.main 入口函数
可以说,Goroutine 是 Go 最显著的特征。它用类协程的方式来处理并发单元,却又在运行时层面做了更深度的优化处理。这使得语法上的并发编程变得极为容易,无须处理回调,无须关注线程切换,仅一个关键字,简单而自然。
搭配channel,实现 CSP 模型。将并发单元间的数据合拆解开来,各司其职,这对所有纠结于内存共享、粒度的开发人员都是一个可期盼的解脱。若说有所不足,那就是应该有个更大的计划,将通信从进程内拓展到进程外,实现真正意义上的分布式。
内存分配
将一切并发化固然是好,但带来的问题同样很多。如何实现高并发下的内存分配和管理就是个难题。好在 Go 选择了 tcmalloc[ tcmalloc全称Thread-Caching Malloc,即线程缓存的mallog,实现了高效的多线程内存管理1,它本就是为并发而设计的高性能内存分配组件
可以说,内存分配器是运行时三大组件里变化最少的部分。创去因配合垃圾回收器而修改的内容,内存分配器完整保留了tcmalloc 的原始架构。使用 cache 为当前执行线程提供无分配,多个centra 在不同线程间平衡内存单元复用。在更高层次里,heap 则管理着大块内存,用以切分成不同等级的复用内存块。快速分配和二级内存平衡机制,让内存分配器能优秀地完成高压力下的内存管理任务
在最近几个版本中,编译器优化卓有成效。它会竭力将对象分配在栈上,以降低垃圾回收压力,减少管理消耗,提升执行性能。可以说,除偶尔因性能问题而被迫采用对象池和自主内存管理外,我们基本无须参与内存管理操作。
垃圾回收
垃圾回收一直是个题。早年间,lava]就因垃圾回收低效被嘲笑了许久,后来 Sun 连续收纳了好多人才和技术才发展到今天。可即便如此,在Hadoop等大内存应用场景下,垃圾回收依旧捉襟见时、步履维艰
相比)ava,Go 面临的困难要更多。因指针的存在,所以回收内存不能做收缩处理。幸好,指针运算被阻止,否则要做到精确回收都难,
每次升级,垃圾回收器必然是核心组件里修改最多的部分。从并发清理,到降低 STW 时间,直到G0 的 1.5 版本实现并发标记,逐步引入三色标记和写屏障等等,都是为了能让垃圾回收在不影响用户逻辑的情况下更好地工作。尽管有了努力,当前版本的垃圾回收算法也只能说堪用,离好用尚有不少距离。
静态链接
Go刚发布时,静态链接被当作优点宣传。只须编译后的一个可执行文件,无须附加任何东西就能部署。这似乎很不错,只是后来风气变了。连着几个版本,编译器都在完善动态库buildmode功能,场面一时变得有些尴尬。暂不说未完工的 buildmnode 模式,静态编译的好处显而易见。将运行时、依赖库直接打包到可执行文件内部,简化了部署和发布操作,无须事先安装运行环境和下载诸多第三方库。这种简单方式对于编写系统软件有着极大好处,因为库依赖一直都是个麻烦。
标准库
功能完善、质量可靠的标准库为编程语言提供了充足动力。在不借助第三方扩展的情况下,就可完成大部分基础功能开发,这大大降低了学习和使用成本。最关键的是,标准库有升级和修复保障,还能从运行时获得深层次优化的便利,这是第三方库所不具备的.
Go 标准库虽称不得完全覆盖,但也算极为丰富。其中值得称道的是 net/hto,仅须简单几条语句就能实现一个高性能 Web Server,这从来都是宣传的亮点。更何况大批基于此的优秀第三方 Framework 更是将 Go推到Web/Microservice 开发标准之一的位置。
当然,优秀第三方资源也是语言生态圈的重要组成部分。近年来崛起的几门语言中,Go 算是独树一帆,大批优秀作品频警涌现,这也给我们学习Go 提供了很好的参照。
工具链
完整的工具链对于日常开发极为重要。Go 在此做得相当不错,无论是编译、格式化、错误检查、帮助文档,还是第三方包下载、更新都有对应的工具。其功能未必完善,但起码算得上简单易用。
内置完整测试框架,其中包括单元测试、性能测试、代码覆盖率、数据竞争,以及用来调优的 pprof,这些都是保障代码能正确而稳定运行的必备利器。
除此之外,还可通过环境变量输出运行时监控信息,尤其是垃圾回收和并发调度跟踪,可进一步帮助我们改进算法,获得更佳的运行期表现。