面试题答案
一键面试可能遇到的问题
- 资源未正确释放:如果在 goroutine 中打开了文件、连接数据库等操作,在 Context 取消时,这些资源可能没有正确关闭或释放,导致资源泄漏。
- goroutine 泄漏:即使 Context 取消了,但如果 goroutine 没有正确响应取消信号,它可能会继续运行,占用系统资源。
- 嵌套 goroutine 处理不当:当一个 goroutine 启动多个子 goroutine 时,如果没有正确传递 Context,父 goroutine 取消时,子 goroutine 可能不会收到取消信号,继续运行。
解决方法
- 资源释放:在 goroutine 中,使用
defer
语句来确保在函数返回时释放资源。例如,打开文件后,使用defer file.Close()
。 - 正确响应取消信号:在 goroutine 的主循环中,定期检查
ctx.Done()
通道是否关闭。如果关闭,则立即返回,停止 goroutine 的执行。 - 传递 Context:在启动子 goroutine 时,将父 goroutine 的 Context 传递给子 goroutine,确保子 goroutine 能收到取消信号。
示例代码
package main
import (
"context"
"fmt"
"io/ioutil"
"os"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
go readFile(ctx)
time.Sleep(3 * time.Second)
}
func readFile(ctx context.Context) {
file, err := os.Open("test.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
data := make([]byte, 1024)
for {
select {
case <-ctx.Done():
fmt.Println("Context cancelled, exiting goroutine")
return
default:
n, err := file.Read(data)
if err != nil {
fmt.Println("Error reading file:", err)
return
}
fmt.Println("Read", n, "bytes from file")
}
}
}
在上述代码中:
context.WithTimeout
创建了一个带有超时的 Context,2 秒后自动取消。readFile
函数打开文件,并在循环中读取文件内容。- 使用
select
语句监听ctx.Done()
通道,当 Context 取消时,立即返回,确保 goroutine 正确结束,并通过defer file.Close()
确保文件资源正确释放。