MST
星途 面试题库

面试题:Go协程错误处理与资源释放

假设有一个Go程序,使用协程处理文件读写操作,每个协程负责读取一个文件并处理其内容,处理完成后需要关闭文件。在这个过程中,文件读取可能会出错,而且如果在出错时没有正确关闭文件,可能导致资源泄漏。请设计一个机制,既能有效处理协程中的文件读取错误,又能保证在任何情况下文件都能被正确关闭。详细说明设计思路并给出关键代码片段。
12.0万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 使用defer关键字:在Go语言中,defer语句用于注册一个延迟执行的函数,该函数会在包含它的函数返回前执行。在打开文件后,立即使用defer来注册关闭文件的操作,这样无论函数是正常返回还是因为错误提前返回,文件都会被关闭。
  2. 错误处理:在文件读取过程中,对每一个可能返回错误的操作进行检查。如果发生错误,通过error类型返回错误信息,调用方可以据此进行处理。
  3. 使用WaitGroup:因为是多协程操作,使用sync.WaitGroup来等待所有协程完成任务,确保在程序退出前所有文件操作都已完成。

关键代码片段

package main

import (
    "fmt"
    "io/ioutil"
    "sync"
)

func processFile(filePath string, wg *sync.WaitGroup) {
    defer wg.Done()

    data, err := ioutil.ReadFile(filePath)
    if err != nil {
        fmt.Printf("Error reading file %s: %v\n", filePath, err)
        return
    }

    // 处理文件内容
    fmt.Printf("Processed file %s: %s\n", filePath, data)
}

func main() {
    var wg sync.WaitGroup
    filePaths := []string{"file1.txt", "file2.txt", "file3.txt"}

    for _, filePath := range filePaths {
        wg.Add(1)
        go processFile(filePath, &wg)
    }

    wg.Wait()
    fmt.Println("All files processed.")
}

在上述代码中:

  • processFile函数负责单个文件的读取和处理。在函数开始处使用defer wg.Done()确保协程完成时WaitGroup计数减一。
  • ioutil.ReadFile用于读取文件内容,若读取失败,会打印错误信息并返回。
  • main函数中,通过wg.Add(1)为每个协程增加计数,并使用go关键字启动协程。最后通过wg.Wait()等待所有协程完成。

如果需要更底层的文件操作(如使用os.Openfile.Read),代码如下:

package main

import (
    "fmt"
    "os"
    "sync"
)

func processFile(filePath string, wg *sync.WaitGroup) {
    defer wg.Done()

    file, err := os.Open(filePath)
    if err != nil {
        fmt.Printf("Error opening file %s: %v\n", filePath, err)
        return
    }
    defer file.Close()

    var buf [1024]byte
    for {
        n, err := file.Read(buf[:])
        if n > 0 {
            // 处理读取到的数据
            fmt.Printf("Read from %s: %s\n", filePath, buf[:n])
        }
        if err != nil {
            break
        }
    }
}

func main() {
    var wg sync.WaitGroup
    filePaths := []string{"file1.txt", "file2.txt", "file3.txt"}

    for _, filePath := range filePaths {
        wg.Add(1)
        go processFile(filePath, &wg)
    }

    wg.Wait()
    fmt.Println("All files processed.")
}

在这个版本中:

  • 使用os.Open打开文件,并立即使用defer file.Close()确保文件关闭。
  • 通过file.Read循环读取文件内容,处理读取到的数据,并在读取错误时跳出循环。