select是随机的还是顺序的?
select会随机选择一个可用通道做收发操作
Go语言局部变量分配在栈还是堆?
Go语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做逃逸分析,当发现变量的作用域没有跑出函数范围,就可以在栈上,反之则必须分配在堆。
go语言中的引用类型和值类型包含哪些?
引用类型:组切片、字典(map)、通道(channel)、接口(interface)
值类型:基本数据类型,int,float,bool,string,以及数组和struct
说说go语言中的init函数?
(1) 一个包中,可以包含多个init函数
(2) 程序编译时,先执行导入包的init函数,再执行本包内的init函数
go语言中的struct能比较吗?
答案:可以能、也可以不能
如果结构体的所有成员变量都是可比较的,那么结构体就可比较
如果结构体中存在不可比较的成员变量,那么结构体就不能比较
说说go语言的channel特性?
(1)、给一个 nil channel 发送数据,造成永远阻塞
(2)、 从一个 nil channel 接收数据,造成永远阻塞
(3) 、给一个已经关闭的 channel 发送(写入)数据,引起 panic
(4)、从一个已经关闭的 channel 接收(读取)数据,如果缓冲区中为空,则返回一个零值
(5)、无缓冲的channel是同步的,而有缓冲的channel是非同步的(这个经常问到)
channel的实现原理
channel本质是个结构体,此结构体 维护了 3个队列,具体如下:
channel的实现原理,本质上就是 协程间对 一个固定长度的环形队列 读写操作;
队列满了就不能写入,写入的协程要暂存到 写入队列中;
队列空了就不能读取,读取的协程要暂存到 读取队列中;
协程,线程,进程的区别?
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
map是线程安全的吗
不是线程安全的。在同一时间段内,让不同 goroutine 中的代码,对同一个字典进行读写操作是不安全
的。在map中存入的数据是无序的(map内部实现)。而每次从map中读取的数据也是无序的,因为golang在设计之初,map迭代器的顺序就是随机的。
Context的使用场景
context在Go1.7之后就进入标准库中了。它主要的用处如果用一句话来说,是在于控制goroutine的生命周期。当一个计算任务被goroutine承接了之后,由于某种原因(超时,或者强制退出)我们希望中止这个goroutine的计算任务,那么就用得到这个Context了。
context.go 包中提供了4个以 with 开头的函数这几个函数的主要功能是实例化不同类型的context
通过Background() 和 TODO() 创建empty实例,通常作为根节点
通过WithCancel() 创建cancelCtx 实例
通过WithValue() 创建valueCtx实例
通过WithDeadline 和 WithTimeout 创建timerCtx 实例
1.如果我们需要监控多个 goroutine 是否运行结束(取消多个 goroutine),通常会使用 context,当然使用 context 也可以用于监控一个 goroutine 是否运行结束(取消一个 goroutine),用作取消 context,除此之外,可用作取消 Context 的函数还有 WithDeadline 函数和 WithTimeout 函数,分别用于定时取消和超时取消。
2.在 Golang 语言中,Context 包还有一个重要的作用,就是用作上下文信息传递,使用WithValue 函数传递上下文信息。我们可以在子 goroutine 中调用 Value 方法获取传递的上下文信息。
slice的len、cap、扩容问题
下面代码执行结果是什么?
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s)
// 截取切片使其长度为 0
s = s[:0]
printSlice(s)
// 拓展其长度
s = s[:4]
printSlice(s)
// 舍弃前两个值
s = s[2:]
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
输出结果:
len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]
使用append函数时容量不够会自动扩容
扩容机制
规则1
当需要的容量大于原切片容量的两倍时,会使用需要的容量作为新容量;
当原切片长度小于1024时,新切片的容量会直接翻倍;
而当原切片的容量大于等于1024时,会反复地增加25%,也就是变为原来容量的1.25倍,直到新容量超过所需要的容量。
规则2
如果扩容之后还没有超过原数组的容量,那切片中的指针指向的位置还是原数组;
如果扩容之后超过了原数组的容量,那就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。
Golang中的Slice与数组的区别
1.slice长度可变,数组是固定长度
2.slice是引用类型,数组是值类型
3.slice可以使用make创建
new()和make()的区别
new(T) 会为 T 类型的新值分配已置零的内存空间,并返回地址(指针),即类型为 *T 的值,不能对指针执行 append 操作,适用于值类型,如数组、结构体等。
make(T,args) 返回初始化之后的 T 类型的值,这个值并不是 T 类型的零值,也不是指针 *T,是经过初始化之后的 T 的引用。make() 只适用于 slice、map 和 channel.
map如何顺序读取?
可以把key放入切片,对切片进行排序,遍历切片,通过key取值。
如何定义一个包内全局字符串变量?
A. var str string
B. str := “”
C. str = “”
D. var str = “”
参考答案:AD
如何通过指针变量 p 访问其成员变量?
A. p.name
B. (*p).name
C. (&p).name
D. p->name
参考答案:AB
局部变量的初始化方式
A. var i int = 10
B. var i = 10
C. i := 10
D. i = 10
参考答案:ABC
const常量定义?
A.
const Pi float64 = 3.14159265358979323846
const zero= 0.0
B.
const (
size int64= 1024
eof = -1
)
C.
const (
ERR_ELEM_EXISTerror = errors.New("element already exists")
ERR_ELEM_NT_EXISTerror = errors.New("element not exists")
)
D.
const u, vfloat32 = 0, 3
const a,b, c = 3, 4, "foo"
参考答案:ABD
test要求测试函数的前缀必须命名为?
参考答案:Test
golang支持goto语句吗?
参考答案:支持
panic仅有最后一个可以被revover捕获?
参考答案:正确,最后的panic会覆盖之前的panic
Redis的数据类型?
参考答案:Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)
runtime包用过哪些函数?
参考答案:
runtime.GC():主动进行一次垃圾回收
debug.SetMaxThreads():对go语言运行时系统所使用的内核线程的数量进行设置
runtime.Goexit():立即使调用他的Groution的运行被终止,但其他Goroutine并不会受到影响
Goroutine的调度原理?
参考答案:基于G-P-M 模型
G: 它是Goroutine,存储了Goroutine的执行栈信息、Goroutine状态以及Goroutine的任务函数等(G是可以重用的)。
P: 它是逻辑Processor,P的数量决定了系统内最大可并行的G的数据(物理CPU核数>=P的数量);P最大的作用是它有各种G对象队列、链表、缓存和状态。
M: 它是真正执行计算的资源。在绑定有效的P后,一个调度循环开始;而调度循环的机制是从各种队列、P的本地运行队列中获取G,切换到G的执行栈上并行执行G的函数,调用goexit做清理工作,然后回到M。这样反复。M并不保存G的状态,这是G可以跨M调度的基础。
…参考 https://blog.csdn.net/weixin_43633725/article/details/112940058
MySQL的优化?
参考答案:
1.建立索引
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将引擎放弃使用索引而进行全表扫描
3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描
5.in 和 not in 也要慎用,否则会导致全表扫描
6.尽可能的使用 varchar 代替 char ,因为首先变长字段存储空间小,可以节省存储空间
7.MySQL集群、负载均衡、读写分离
8.尽量使用整型表示字符串
9.不要写一些没有意义的查询,如需要生成一个空表结构:select col1,col2 into #t from t where 1=0
各种排序算法go语言版
http://www.topgoer.com/Go%E9%AB%98%E7%BA%A7/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.html
其他面试题精选
http://wen.topgoer.com/docs/gomianshiti/mianshiti