七叶笔记 » golang编程 » go语言学习的几个概念:接口、协程、通道、WaitGroup

go语言学习的几个概念:接口、协程、通道、WaitGroup

因为儿子在学校自学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!

相关文章