前言
基础语法部分在前面学习笔记内已经讲述了,这里就不再赘述了,只会在讲特性的时候会带上部分语法知识,这里主要是讲常用的特性解析,我看过好多资料,每本书有每本书的说法,我截取了其中我觉得最能总结Go语言特点特性的几个词,分别是:追求简单,少即是多;偏好组合,正交解耦;原生并发,轻量高效;
引用
[1] 白明 著. GO语言精进之路:从新手到高手的编程思想、方法和技巧(Go Programming from Beginners to Masters Thinking,Methods and Techniques) [M],北京:机械工业出版社, 2022.1
追求简单,少即是多
简介
大部分Gopher选择Go语言都是因为其简单,不仅仅是指语法简单,很多地方都有体现出其简单性,这是因为在设计之初,Go的设计者就拒绝走语言特性融合的道路,他们把复杂性留给了语言自身的设计和实现,留给了Go语言核心开发组自己,而将简单、易用和清晰留给了我们。
具体体现
-
简洁、常规的语法(不需要解析符号表),它仅有25个关键字;
-
没有类似于c或c++的头文件,没有指针算术,也没有类型注解语法(比如c++里的const、static等);
-
没有Java中类的概念,任何类型都可以拥有方法,没有子类型的继承,并且没有类似于Java的构造函数和析构函数,没有异常,
-
有独有的错误处理机制,并且将错误当做一个值来处理等
......
-
-
内置垃圾收集,降低开发人员内存管理的心智负担,Go语言的自动回收内存的特性,使得Go程序开发者可以更加专注于代码的效率,很大程度上避免了内存的泄露;
-
内置并发支持,我们只需要通过一个简单的关键字"go"就可以实现并发;
理解
我觉得这体现的就是Go语言的一种简单性,这个是Go语言本身给我们带来的便利,Go语言本身实现的过程是非常复杂的,但是这个结果就是能让我们更加便利的使用Go语言,我们可以用更加简单的语法去实现对应的功能,当然前提是得知道对应的语法知识
偏好组合,正交解耦
简介
C++,Java等主流面向对象语言通过庞大的自下而上的类型体系、继承、显示接口实现等机制将程序的各个部分耦合起来,但在Go语言中我们找不到这种语法元素,类型体系和继承机制,那Go语言是如何将各个部分有机地耦合起来的呢?
正交的语法元素
- Go语言无类型体系,类型之间是独立的,没有子类型的概念
- 每个类型都可以有自己的方法集合,类型定义与方法实现是正交独立的
- 接口与其实现之间隐式关联
- 包之间是相互独立的,没有子包的概念
组合
Go采用了组合的方式将各个部分耦合,这也是唯一的一种方式
垂直组合
- 介绍:通过类型嵌入,快速让一个新类型复用其他类型已经实现的功能,实现功能的垂直扩展。类似于继承,但是原理完全不一样
- 具体表现:我们可以通过类型嵌入,将已经实现的功能嵌入新类型中,以快速满足新类型的功能需求,被嵌入的类型和嵌入的类型之间互不相关,举例如下:
type poolLocal struct{
private interface{}
shared []interface{}
Mutex
pad [128]byt
}
我们在poolLocal这个结构体类型中嵌入了类型Mutex,被嵌入的Mutex类型的方法集合会被提升到poolLocal类型中。例如,这里的poolLocal将拥有Mutex类型的Lock和Unlock方法。但在实际调用时,方法调用会被传给poolLocal内的Mutex实例
水平组合
-
介绍:通过interface将程序各个部分组合在一起的方法;interface只是方法的集合,并且和实现之间是隐式相关的,可以使得程序各个部分之间的耦合降至最低,同时又是各个部分连接的纽带。
-
具体表现:水平组合常用的方法,通过接受interface类型参数的普通函数进行组合,例如下面的代码:
func ReadAll(r io.Reader) ([]byte,error)函数ReadAll通过io.Reader这个接口将io.Reader的实现与ReadAll所在的包以低耦合的方式水平组合在了一起
理解
这里讲述的就是go语言是如何将各个部分连接起来的,其实可以这样理解,go语言的各个部分都是相互独立互不干扰的,相当于一个个岛屿一样没有联系,但是我们可以通过组合来将各个岛屿之间建立联系,而根据实现联系的方法不同就分为垂直组合和水平组合,就类似于继承和接口的思想,但是实现的原理又完全不一样,实现的方法也比其他语言简单,并且可以降低耦合
原生并发,轻量高效
简介
Go的设计者敏锐地把握了CPU向多核方向发展的这一趋势,将面向多核,原生内置并发支持作为Go语言的设计原则之一
具体表现
-
Go语言采用轻量级协程并发模型,使得Go应用在面向多核硬件时更具可扩展性,传统的基于操作系统线程的并发模型会有两大不足,复杂和难于扩展,复杂体现在其中创建线程容易,线程的退出就会面临很多问题,难于扩展体现在不能大量的创建线程。所以Go语言采用了用户层轻量级线程或者说是类协程,也就是goroutine。我们知道goroutine分配的栈空间只有2KB。所以我们在一个Go程序中可以创建成千上万个并发的goroutine。所有的Go代码都可以在goroutine中执行。但是Go程序对操作系统来说只是一个用户层程序。操作系统不能识别goroutine,只能识别线程。所以我们不能通过操作系统去调度这个goroutine,而调度goroutine这个任务就归Go所有,Go运行时,得将这些goroutine公平竞争CPU的资源,当然其中肯定会涉及到调度的算法,这个时候Go语言就有专门一个程序实现这个算法,将这些goroutine安排到CPU上执行,这个程序就是goroutine调度器。
-
并发的语法元素和机制,还是以传统的编程语言为例看他们是如何实现并发的,首先它们的单元是线程,它们通过调用库函数或调用对象方法来创建或销毁线程,然后线程之间的通信,它们大部分也是基于操作系统的IPC机制来实现的;而Go语言,我们学过goroutine,我们知道这个是Go的基本单元,然后我们可以直接通过go命令+函数调用来创建,退出就直接退出函数就是退出goroutine,而goroutine之间的通信,我们都知道是通过共享通信来共享内存,也就是通过channel来实现的,相比来看,Go语言更加省事。
-
并发原则对我们在程序结构设计层面的影响,虽然我们都知道用goroutine,也知道这个开销相对于线程小很多,但是这并不意味着我们一定能够充分利用这个多核资源,也并不一定能写出好的并发程序,并行和并发是两个不同概念的词,并发是有关结构的,而并行是有关执行的,我们可以通过并发使得并行成为可能。而我们使用的并发的方案不仅仅只在单核的情况下可以实现高效,当我们处理器核数增加时,我们的并发方案也可以自然地提高处理性能,自然的扩展,充分提升多核利用率。
理解
Go语言的并发是原生的,而且轻量高效,我们学习的goroutine就是轻量且高效的,但是如果我们要做到并行,我们设计并发方案要以在多核情况下充分提升多核利用率、获得性能的自然提升为最终的目的
总结和建议
总结
这里分析的三个大特性,我结合了就是书Go语言设计之初设计者的思想,我觉得从这些方面去解析这些特性,我们可以更好的知道和理解这些特性的由来,以及这些特性运用时的优点,首先是简单性,简单是贯穿整个Go语言的,整个Go语言的主旨设计哲学,我们可以通过一些对比可以知道其简单性,包括垃圾回收,错误处理机制等,其次就是组合,组合我觉得是非常重要的,是项目各部分连接起来的纽带,也是其中非常重要的特性,最后就是并发,Go语言对并发特性这一块改进很多,而且是面向多核的,实现起来更加简单,并且更加高效。
学习建议
这里我讲述的都是理论知识,其实真正的去接触一个项目,去做一个项目,在做这个项目的时候用到这个特性的时候就会有不一样的感受,所以这个得实操,多实操才能更加深入的了解