golang基础面试题总结
前言:由于正在准备之后的实习面试,故总结了一部分golang语言基础的问题,回答全为自己组织的语言,若有错各位大佬可及时指出,大家共同进步,谢谢。
1.go中怎样实现安全读写共享变量
要实现安全读写共享变量,也可以理解为在有一定竞争条件下保证并发读写的安全性,这就可以使用到go语言的一部分并发组件来达到这个目的了,首先是加锁,也就是加RWMutex互斥读写锁。其次是通过channel进行通信以实现共享内存空间来原子性的读写共享变量。
2.无缓冲的chan的发送和接收是否同步
是同步的,因为go语言中无缓冲的channel是阻塞的,只有channel中的数据被消费后,新数据才能写入。而从空channel中读取数据的goroutine则需要阻塞并等待一条数据的写入,这也就达到了发送和接收的同步。
3.Golang并发机制以及它所使用的CSP并发模型
由于golang中的并发机制的主要灵感则来自于CSP的原始论文和之后发展而来的并发原语,所以在此我先谈谈对CSP并发模型的理解。CSP全称是是通信顺序进程。所以CSP讲究的是“以通信的方式来共享内存”,用于描述两个独立的并发实体通过共享的 channel(管道)进行通信的并发模型。
golang中实现的channel就是被单独创建的用于在两个不同实体间进行消息传递。由于这种通信模式只注重于发送接收消息的管道而不是其中的实体,这也造就了实体间的解耦合。实体所发出的消息最终一定会被另一个实体所消费,也是类似于一种阻塞的消息队列。
而golang中的goroutine其实就是这种实际并发执行中实体的一个抽象,底层是使用协程进行并发。这里也就引出了协程的概念,golang里实现的协程在概念上我理解为一种在用户态运行实现的轻量级线程,它的一些区别于线程的特定也是它本身创建使用成本比较轻量级的原因。首先协程是运行在用户空间的,由golang自己的调度器实现调度,而这个调度器是在用户空间运行的,切换时不会trap进内核,这就直接避免了内核态和用户态的切换成本。而线程受内核调度,每次定时器中断造成的切换都需要保存寄存器、PC、栈等等信息,耗费的时间成本很高。其次goroutine在开启生命周期时创建的栈空间很小,一般只需要2K,并且大小会根据使用情况动态伸缩,最大可以扩充到1G,这就是goroutine轻量级的原因。
说回goroutine的特性,golang内部使用的是GMP调度模型,所对应的就是协程、线程、处理器。默认的单核情况下,创建的所有goroutine都先运行在一个线程中。每个线程都维护一个单独的处理器,任意时刻一个处理器中都只运行一个goroutine,其他的goroutine都在一个工作队列中等待。如果当前goroutine阻塞,就再创建一个线程并把处理器转移到新线程上运行,当原线程阻塞结束之后,会在其他线程上重新寻找一个空闲的处理器,否则就入到工作队列中。
所以综上,golang中的并发机制是以CSP作为基础理论根据,通过GMP调度模型也可以概括为事件循环、线程池、工作队列三个结构来调度相应的并发组件channel和goroutine以实现并发。
4.Golang中常用的并发模型
golang中常用的并发模型:
首先是for-select和channel的组合,有几种情况很适用,一种是需要迭代的向channel发送变量值。第二种是循环的等待停止,不管是有条件的有限循环还是无限循环都适用,比如值得一提的是用自带包中的ticker作时钟来实现一些定时的时间事件。
其次是使用sync包中的一些并发原语的组合类型,比如可以等待一组并发操作完成的waitGroup,调用Add(1)来表明goroutine的开始,defer Done()来保证当前goroutine向waitGroup表明已经安全退出。最后可以在main goroutine中调用wait()来阻塞等待waitGroup中所有的goroutine正确退出。还有就是用mutex锁来独占临界区嘛,比较简单。还有值得一提的就是cond类型,它可以阻塞的暂停挂起当前的goroutine等待或者发布一个事件。如果不使用cond原语来实现这种功能的话,最简单的方法就是使用一个无限循环,但是这会消耗整个cup的周期来等待条件的发生,而cond只会挂起当前g

本文总结了Go语言的基础面试问题,涵盖并发控制、安全共享变量、channel同步、CSP模型、goroutine特性、内存管理、垃圾回收、锁与并发控制模型,以及关键数据结构如slice、map和channel的实现原理。
最低0.47元/天 解锁文章
1780

被折叠的 条评论
为什么被折叠?



