常见的 Go 语言面试题及其答案和代码示例:
一、高频面试题
1. Goroutine 和线程的区别?
答案:
- Goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理,初始栈大小约为 2KB,可以动态扩展和收缩,创建和切换成本非常低。
- 线程 是操作系统级别的线程,栈大小通常为 MB 级,创建和切换涉及内核态和用户态的切换,成本较高。
- Go 使用 M:N 调度模型,将多个 Goroutine 映射到少量的操作系统线程上,从而高效利用 CPU 资源。
2. 如何避免 Goroutine 泄漏?
答案: 使用 context.Context
或通道来传递退出信号,确保 Goroutine 在完成任务后能够正确退出。
ctx, cancel := context.WithCancel(context.Background())
go func() {
select {
case <-ctx.Done(): // 接收取消信号
fmt.Println("Goroutine 退出")
return
case <-time.After(2 * time.Second):
fmt.Println("任务完成")
}
}()
time.Sleep(1 * time.Second)
cancel() // 触发退出
time.Sleep(1 * time.Second) // 等待 Goroutine 退出
3. defer 的执行顺序?
答案: defer
语句按照后进先出(LIFO)的顺序执行。
func main() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
// 输出:3 2 1
}
4. defer 如何影响返回值?
示例:
// 命名返回值:返回 1
func f() (result int) {
defer func() {
result++ }()
return 0
}
// 匿名返回值:返回 0
func f() int {
result := 0
defer func() {
result++ }()
return result
}
5. 实现一个 Worker Pool
代码示例:
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("Worker %d 处理任务 %d\n", id, j)
results <- j * 2
}
}
func main() {
jobs := make(chan int, 10)
results := make(chan int, 10)
// 启动 3 个 Worker
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// 发送任务
for j := 1; j <= 5; j++ {
jobs <- j
}
close