因为儿子在学校自学go语言编程,时不时为他解答一些问题,也让很长时间没写过代码的我不懂的时候重温一下编程知识。虽然语言是新的,但好像编程思维还在。也试图通过一些易理解的例子来解释其中一些概念,虽然不会很准确,但也许也可以促进理解。
关于接口的解释
type <接口名称> interface {
<方法列表>
}
这是go语言中对接口的定义语法,其实所有语言关于接口的定位都是一致的,定义方式也是大同小异。用下面的例子来解释“接口是什么,为什么要定义接口”:
以衣服为例:衣服要有两个袖子、五粒扣子。只有符合这个标准的衣服人才能穿进去,这个可以理解为接口(标准)。然后有白色衬衣、棉袄、夹克不同的类型,不同衣服的材料不一样,颜色不一样,厚度不一样,长度不一样。这些不同衣服都要符合上面那个接口标准(两个袖子和五粒扣子),才能有人买了穿。
然后就是不同人在不同季节买衣服的时候,会根据实际需要买不同的衣服。但不同人在跟别人交流的时候,他可能只会说,我穿了衣服。如某个场所要求进入的人只要穿了衣服就行,而不需要管你穿什么衣服。
另一个手机充电接口的例子:Adroid手机的充电接口现在都基本统一成typec接口了,所以相互之间可以通用,但不同的充电器实现方式不一样,有快速充电有多少伏充电。苹果手机的充电器无法给华为手机充电,那是因为接口不一样。
程序直接相互调用的时候,只需要使用接口来调用,不需要知道里面实现的细节。一个系统会有很多模块组成,不同模块实现不同功能,这些模块之间就要定义接口规范。在不同场景下,功能实现会有差别,但模块跟模块之间的调用方式不变,这就依赖于接口规范。
上升到更高层面就是架构设计需要考虑的问题,不管我怎么变,我们的交互方式不要变。所以一个系统的架构设计中,接口就是其很重要的一部分。
关于协程和通道的解释
为了简单理解go语言中的协程和通道的概念,用公路上的行驶车辆来做比方。
Go语言的协程概念就是一个轻量级的线程,它不依赖于操作系统的调度,只依赖于当前运行的进程或线程。我理解的进程(主线程)和线程是依赖于操作系统的调度机制,线程都是通过CPU分时调度的。对协程而言,其实也是线程在应用层的分时调度,而不是CPU的分时调度,如果线程消亡,这个线程中启动的协程就会自动消亡。
下面这个公路的例子,我们需要先忽略这个分时调度的概念,暂时认为是可以同时运行的,因为不同辅道的车辆是可以同时行驶的,而实际上的协程中的内容是分时处理的,抛开“分时”这个概念来理解协程和通道,可能更容易。
某一段公路有三条道(一条主道,左右各一条辅道),正常情况下三条道的车辆都各自通行,这里辅道一定是依赖于主道的存在的,没有主道就谈不上辅道的概念,即辅道源于主道、终于主道。
由于道路容量限制当两条辅道上各有100辆车在行驶时,为了减轻两条辅道的行驶压力,有一名交警在进行指挥,左边辅道排在奇数位和右边辅道排在偶数位的车辆需要转入主道,而且需要依次轮流转入主道,同时需要对主道进行交通管制。
用个代码例子来解释:
var ch chan int //两条辅道的轮流通行指示,辅道上的车辆只能依次通行。
var exit chan string //在两条辅道进行交通指挥时,对主通道进行交通管制的指示。
func print1() {//左边辅道上有100辆车,奇数位车辆需要进入主道
for i := 0; i <= 100; i++ {
ch <- 1//左边辅道在交警指挥下通行,右边辅道禁止通行。
if i%2 == 1 {
fmt.Println(i)
}
}
}
func print2() {//右边辅道上有100辆车,偶数位车辆需要进入主道
for i := 0; i <= 100; i++ {
<-ch//右边辅道在接受到交警指挥后通行,左边辅道就禁止通行。
if i%2 == 0 {
fmt.Println(i)
}
if i == 100 {//当全部符合条件的车辆转入主道后,主道管制解除
exit <- “x”//主通道管制解除指示
}
}
}
func main() {
ch = make(chan int) //创建一个channel,作为两条辅道的交通指示
exit = make(chan string)//创造一个channel,主道的管制指示
go print1()
go print2()
<-exit//主通道管制解除
}
关于WaitGroup的应用
sync.WaitGroup类型内部维护一个计数器,某个Go协程调用Wait方法后会被阻塞,直到WaitGroup对象的计数器变为0。
下面这段代码跟上面这节程序的功能是类似的,只是用WaitGroup实现了“通道”的功能,让主线程等待其他协程运行完成。
var ch chan int
var wg sync.WaitGroup
func print1() {
for i := 0; i <= 100; i++ {
ch <- 1
if i%2 == 1 {
fmt.Println(i)
}
}
wg.Done()
}
func print2() {
for i := 0; i <= 100; i++ {
<-ch
if i%2 == 0 {
fmt.Println(i)
}
}
wg.Done()
}
//主线程:当主线程执行完毕后会直接退出,协程也会退出,尽管它可能没执行完毕
//可以使用sync.WaitGroup可以实现主线程等待协程执行完毕
func main() {
ch = make(chan int)
wg.Add(2)
go print1() //表示开始一个协程,go语言的优势就在于开启协程所占用的空间非常小
go print2()
wg.Wait()
}
OVER!