面试题答案
一键面试避免文件描述符耗尽
- 限制并发数:使用
sync.WaitGroup
和一个通道来限制同时打开文件的 goroutine 数量。通道的缓冲区大小决定了允许同时打开文件的最大数量。 - 及时关闭文件:在使用完文件后,立即调用
file.Close()
方法关闭文件,释放文件描述符。
处理文件读写的并发冲突
- 互斥锁:使用
sync.Mutex
来保护对文件的读写操作。在进行读写之前锁定互斥锁,操作完成后解锁。 - 读写锁:如果读操作远多于写操作,可以使用
sync.RWMutex
。读操作使用读锁(RLock
),写操作使用写锁(Lock
)。
优雅地关闭文件资源
- defer 关键字:在打开文件后,使用
defer file.Close()
确保在函数返回时文件会被关闭,即使发生错误。 - context 包:结合
context.Context
,在取消上下文时,及时关闭文件并清理资源。
示例代码
package main
import (
"context"
"fmt"
"io/ioutil"
"log"
"sync"
)
var (
fileMutex sync.Mutex
fileLimit = make(chan struct{}, 10) // 限制同时打开10个文件
)
func readFile(ctx context.Context, filePath string) {
// 获取文件打开许可
fileLimit <- struct{}{}
defer func() { <-fileLimit }()
fileMutex.Lock()
data, err := ioutil.ReadFile(filePath)
fileMutex.Unlock()
if err != nil {
log.Printf("Error reading file %s: %v", filePath, err)
return
}
select {
case <-ctx.Done():
log.Printf("Operation cancelled for file %s", filePath)
return
default:
fmt.Printf("Read data from %s: %s\n", filePath, data)
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
filePaths := []string{"file1.txt", "file2.txt", "file3.txt"}
for _, filePath := range filePaths {
wg.Add(1)
go func(fp string) {
defer wg.Done()
readFile(ctx, fp)
}(filePath)
}
// 模拟一段时间后取消操作
go func() {
select {
case <-ctx.Done():
case <-time.After(2 * time.Second):
cancel()
}
}()
wg.Wait()
}
在这个示例中:
fileLimit
通道用于限制同时打开文件的数量,避免文件描述符耗尽。fileMutex
互斥锁用于保护文件读取操作,防止并发冲突。context.Context
用于在需要时优雅地取消操作,并通过select
语句检查上下文是否已取消,以决定是否继续执行文件读取操作。defer
关键字确保文件在使用后被关闭(ioutil.ReadFile
内部会自动处理文件关闭),同时defer
也用于释放fileLimit
通道的许可。