欢迎点击下方👇关注我,记得星标哟~
文末会有重磅福利赠送
今天分享一篇腾讯的面经,面经的主人公有3年的Golang开发经验,岗位的薪资为25-30K
,内容涉及到的知识点很多,我都给它整理好了,看看难度如何:
1. Go的调度机制
主要就是回答GMP模型
G-P-M 模型
G (Goroutine) :代表用户代码中的一个 Goroutine,是 Go 中最小的执行单元。 P (Processor) :每个 P 表示一个逻辑处理器,负责管理一组可运行的 goroutine。默认情况下,P 的数量等于系统的 CPU 核心数,但可以通过 runtime.GOMAXPROCS()
函数调整。M (Machine) :对应于一个真实的操作系统线程,M 执行实际的代码。M 可以获取 P 来执行其上的 goroutine,当 M 阻塞时(例如进行系统调用),它会释放 P,让其他 M 获取并继续执行任务。
工作窃取调度
本地队列:每个 P 拥有一个本地的工作队列,用于存放待执行的 goroutine。这减少了锁争用,提高了性能。 全局队列:除了本地队列外,还有一个全局队列,用于分配新的 goroutine 给各个 P。 工作窃取:当一个 M 关联的 P 上的本地队列为空时,M 会尝试从其他 P 的本地队列中“窃取”一半的任务来执行。这种机制有助于平衡负载,尤其是在多核处理器上。
2. Go的struct能否进行比较
可以,Go中的struct可以进行比较。在Go语言中,结构体类型是可以比较的,只有当结构体中的所有字段都是可以比较的类型时才可以进行比较。如果结构体中的字段包含了不可比较的类型(比如切片、map等),则结构体就不能进行比较。
在进行结构体比较时,会逐个字段进行比较,如果所有字段的值都相等,则认为两个结构体相等。需要注意的是,结构体比较是值比较,即比较的是结构体实例的具体值,而不是引用或指针。
3. Go中的defer关键字使用
defer关键字在Go中用于延迟(defer)函数的执行,即在函数执行完毕后再执行defer函数。defer函数通常用于资源释放、日志记录、错误处理等场景。defer语句会在函数返回之前执行,多个defer语句按照先进后出的顺序执行。
4. select语句的用途
select语句在Go语言中主要用于实现并发控制和通信操作。通过select语句,可以在多个通信操作中选择一个进行执行,而其他通信操作将被阻塞。在Go语言中,select语句通常与channel配合使用,用于在多个channel间进行数据传输和同步操作。
5. context包的作用
1. 超时和截止时间
context
包允许为操作设置超时或截止时间。这对于防止长时间运行的操作(如数据库查询或 HTTP 请求)阻塞整个应用程序非常有用。通过设置超时或截止时间,可以确保在指定时间内未完成的任务被自动取消,从而避免资源浪费。
WithTimeout:用于创建一个带有固定超时的上下文。 WithDeadline:用于创建一个带有绝对截止时间的上下文。
2. 取消操作
context
提供了机制来显式地取消一个或多个 goroutine 的执行。这对于用户取消请求或者父 goroutine 完成工作后通知子 goroutine 停止工作非常有用。
WithCancel:创建一个可以被显式取消的上下文。调用取消函数后,所有监听该上下文的 goroutine 都会收到取消信号并停止执行。 WithValue:可以在上下文中传递键值对,这些值可以在同一请求链中的不同部分之间共享。虽然提供了这种功能,但应谨慎使用,以避免不必要的复杂性和性能开销。
3. 传递请求范围的值
context
可以用来在同一个请求的不同部分之间传递请求范围的数据,例如用户的认证信息、请求ID等。这种方式有助于保持数据的一致性和可追踪性,同时减少了参数列表的长度。
6. client如何实现长连接
要实现client端的长连接,可以使用TCP协议,通过保持连接不断开的方式来实现。
首先,在client端通过socket建立与server端的TCP连接,然后在连接建立后,client端和server端可以进行双向通信。为了实现长连接,client端需要保持连接不断开,可以定时发送心跳包给server端,以保持连接的活跃状态。
7. 主协程如何等待其他协程完成后再操作
使用 sync.WaitGroup
sync.WaitGroup
是一个计数器,用于跟踪需要完成的任务数量。每个启动的 goroutine 应该调用 Add(1) 来增加计数器,当任务完成时调用 Done() 来减少计数器。主协程可以调用 Wait() 来阻塞,直到计数器归零,即所有任务都已完成。
示例说明:
主协程创建一个 WaitGroup 实例,并为每个子协程调用 Add(1)。 每个子协程在完成任务后调用 Done()。 主协程调用 Wait(),等待所有子协程完成。
使用通道
通道(channel
)是 Go 语言中用于 goroutine 之间通信的机制。通过通道,主协程可以接收来自子协程的完成信号,从而知道何时继续执行。
示例说明:
主协程创建一个通道,并启动多个子协程。 每个子协程在完成任务后向通道发送一个信号。 主协程通过 for range 或者固定次数的 for 循环接收这些信号,确保所有子协程都已完成。
8. slice的扩容机制
1.7版本:如果当前容量小于1024,则判断所需容量是否大于原来容量2倍,如果大于,当前容量加上所需容量;否则当前容量乘2。
如果当前容量大于1024,则每次按照1.25倍速度递增容量,也就是每次加上cap/4。 1.8版本:Go1.18不再以1024为临界点,而是设定了一个值为256的
threshold
,以256为临界点;超过256,不再是每次扩容1/4,而是每次增加(旧容量+3*256)/4;当新切片需要的容量cap大于两倍扩容的容量,则直接按照新切片需要的容量扩容;
当原 slice 容量 < threshold 的时候,新 slice 容量变成原来的 2 倍;
当原 slice 容量 > threshold,进入一个循环,每次容量增加(旧容量+3*threshold)/4。
9. map如何顺序读取
在Go中,map是一种无序的数据结构,因此不能保证按照特定顺序进行读取。如果需要按顺序读取map中的键值对,可以先将键按照特定规则排序,然后再按照排序后的键顺序读取对应的值。
10. 如何实现一个set
用map模拟一个set,把值置为struct{},struct{}本身不占任何空间,可以避免任何多余的内存分配。
type Set map[string]struct{}
func main() {
set := make(Set)
for _, item := range []string{"A", "A", "B", "C"} {
set[item] = struct{}{}
}
fmt.Println(len(set)) // 3
if _, ok := set["A"]; ok {
fmt.Println("A exists") // A exists
}
}
11. HTTP GET和HEAD请求的区别
GET
请求会返回请求的资源,包括头部信息和实际数据,而HEAD
请求只返回请求的资源的头部信息,不返回实际数据。这样可以在不需要资源实际内容的情况下,只获取资源的元数据信息,比如文件大小、类型、修改时间等。在一些情况下,使用HEAD请求可以减少网络流量和加快响应速度。
12. HTTP状态码401和403的区别
HTTP状态码
401
代表未授权,表示客户端请求需要进行身份验证,而服务器拒绝了该请求,通常要求用户输入用户名和密码。HTTP状态码
403
代表禁止访问,表示服务器理解了请求,但拒绝执行该请求,通常是因为服务器不允许访问特定资源或者没有权限执行该请求。
13. HTTP keep-alive机制
HTTP keep-alive
机制是指在一次TCP连接中可以传输多个HTTP请求和响应,而不是每次请求都要建立和关闭一个TCP连接。这样可以减少TCP连接的建立和关闭次数,提高网络性能和资源利用率。
14. HTTP是否可以在一次连接中发送多次请求而不等待后端返回
HTTP是一种无状态协议,每个请求和响应之间是独立的,因此在一次连接中发送多次请求是可能的,而且不需要等待后端返回。这种技术被称为
HTTP pipelining
。在HTTP/1.1中是支持pipelining的,但并不是所有的服务器和客户端都支持这个特性。在实际应用中,由于某些服务器或代理可能不支持pipelining,因此可能会导致性能问题或错误的响应。
15. TCP与UDP的区别,UDP的优点及适用场景
TCP与UDP是两种不同的传输层协议。TCP是面向连接的,提供可靠的数据传输,而UDP是无连接的,提供不可靠的数据传输。
TCP和UDP的区别主要体现在以下几个方面:
连接:TCP是面向连接的,需要先建立连接,然后再进行数据传输,而UDP是无连接的,发送数据时无需建立连接。 可靠性:TCP提供可靠的数据传输,能够保证数据的完整性和顺序性,而UDP不提供可靠性,数据传输过程中可能会丢失或乱序。 拥塞控制:TCP具有拥塞控制机制,能够根据网络情况动态调整传输速率,而UDP不具备拥塞控制。 首部开销:TCP的首部开销较大,包含连接状态、序号、确认号等信息,而UDP的首部开销较小,只包含源端口、目的端口和长度等基本信息。
UDP的优点主要包括:
低开销:UDP的首部开销小,传输效率高。 实时性:UDP不需要建立连接,传输速度快,适用于对实时性要求较高的应用场景。 简单性:UDP相对于TCP更简单,实现和维护成本低。
UDP适用场景包括:
实时音视频传输:如在线视频会议、直播等,对实时性要求高。 DNS查询:域名解析过程中的数据传输,要求快速响应。 广播或多播应用:如在线游戏中的数据广播等。
16. time-wait状态的作用
time-wait
状态是指TCP连接关闭后,等待一段时间才能完全释放资源的状态。在这段时间内,系统会保持连接的信息,以便在网络中的数据传输完整,同时避免出现数据混乱或重复的情况。
17. 孤儿进程和僵尸进程的区别
孤儿进程是指父进程先于子进程结束,而子进程成为孤儿进程,此时孤儿进程会被 init 进程(PID为1)接管,并由 init 进程负责回收孤儿进程的资源,保证不会成为僵尸进程。
僵尸进程是指子进程先于父进程结束,而父进程没有及时回收子进程的 PCB(Process Control Block),导致子进程的进程描述符仍然存在,但已经无法运行,此时子进程就会成为僵尸进程。
18. 死锁的条件及如何避免
死锁(Deadlock)是指两个或多个进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。为了发生死锁,必须同时满足四个必要条件,这些条件被称为 Coffman 条件 或 死锁定理,四个必要条件是:互斥条件、占有并等待条件、 不可抢占条件和循环等待条件。
如何避免死锁
为了避免死锁,可以采取不同的策略来破坏上述四个条件中的一个或多个。以下是几种常见的避免死锁的方法:
1. 破坏“占有并等待”条件
一次性分配所有资源:要求每个进程在开始执行前申请它需要的所有资源。如果不能立即获得所有资源,则该进程必须等待,直到所有资源都可用为止。这种方法虽然简单,但可能导致资源利用率低和饥饿问题。 按序分配资源:为资源编号,规定进程必须按照某种顺序(如递增或递减)申请资源。这样可以防止循环等待的发生。
2. 破坏“不可抢占”条件
允许抢占资源:如果一个进程持有的资源不能满足其需求,它可以被强制释放资源,然后重新申请所需的所有资源。这需要设计复杂的资源管理和恢复机制,以确保数据的一致性和完整性。
3. 破坏“循环等待”条件
资源排序法:给所有的资源分配一个全局唯一的编号,要求进程只能按照编号递增的顺序申请资源。这样可以有效地避免循环等待,因为任何一个进程都不会等待比自己当前持有资源编号小的资源。 银行家算法:这是一个更复杂的算法,用于检测系统是否处于安全状态。它通过模拟资源分配来预测未来的资源需求,从而决定是否允许进程继续运行。如果分配后系统仍然处于安全状态,则允许分配;否则拒绝分配并让进程等待。
4. 检测与恢复
死锁检测:定期检查系统中是否存在死锁。如果发现死锁,可以选择一种或多种方法来解除死锁,例如回滚某些进程、杀死某些进程或者重启整个系统。这种方法不需要事先预防死锁,但需要额外的开销来进行检测和恢复。 超时机制:为每个操作设置一个合理的超时时间。如果一个进程在指定时间内没有完成任务,则认为可能发生了死锁,并采取相应的措施。
19. 常用的Linux命令:查看端口占用、CPU负载、内存占用,如何发送信号给一个进程
常用的Linux命令包括:ls(列出目录内容)、cd(切换目录)、pwd(显示当前目录)、cp(复制文件或目录)、mv(移动文件或目录)、rm(删除文件或目录)、mkdir(创建目录)、rmdir(删除空目录)、top(查看系统资源占用情况)、ps(显示当前进程信息)、kill(终止进程)、ifconfig(查看网络接口信息)、netstat(查看网络连接信息)、grep(搜索文本)、tar(打包与解压)、chmod(修改文件权限)等。
要查看端口占用情况,可以使用netstat命令或者lsof命令。netstat -tuln可以查看当前所有TCP和UDP端口的占用情况,而lsof -i:端口号可以查看指定端口的占用情况。
要查看CPU负载,可以使用top命令或者uptime命令。top命令可以实时查看系统资源的占用情况,包括CPU、内存等,而uptime命令可以显示系统的平均负载。
要查看内存占用情况,可以使用free命令或者top命令。free命令可以显示系统内存的使用情况,包括已使用、空闲等信息,而top命令也可以显示内存占用情况。
要发送信号给一个进程,可以使用kill命令。首先使用ps命令找到要发送信号的进程的PID,然后使用kill -信号 PID来发送信号。常用的信号包括SIGTERM(15,终止进程)、SIGKILL(9,强制终止进程)、SIGHUP(1,重启进程)等。
20. Git的文件版本管理,merge和rebase的区别
在Git中,merge和rebase都是用来整合不同分支的修改内容的方法。merge会将两个分支的修改内容合并到一起,形成一个新的提交,而rebase会将当前分支的修改“挪动”到目标分支的最新提交之后。
早日上岸!
我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:面试群。
点击下方文章,看看他们是怎么找到好工作的!