在 Golang 中,并发读取和写入文件可以通过 goroutine 和通道(channel)实现,同时需注意线程安全问题。以下是具体实现方法及代码示例:
1. 并发读取文件核心思路:
- 使用多个 goroutine 并发读取文件的不同部分(如分块读取)。
- 通过通道传递数据,主线程从通道接收并处理结果。
- 使用 sync.WaitGroup 等待所有 goroutine 完成。
代码示例:
package mainimport ( "bufio" "fmt" "os" "sync")func readChunk(filePath string, start, size int64, wg *sync.WaitGroup, ch chan<- string) { defer wg.Done() file, err := os.Open(filePath) if err != nil { fmt.Println("Error opening file:", err) return } defer file.Close() // 定位到文件起始位置 _, err = file.Seek(start, 0) if err != nil { fmt.Println("Error seeking file:", err) return } // 读取指定大小的数据块 reader := bufio.NewReader(file) buffer := make([]byte, size) n, err := reader.Read(buffer) if err != nil { fmt.Println("Error reading chunk:", err) return } ch <- string(buffer[:n])}func main() { filePath := "largefile.txt" fileInfo, err := os.Stat(filePath) if err != nil { fmt.Println("Error getting file info:", err) return } fileSize := fileInfo.Size() // 分块参数:4个goroutine,每块大小 numGoroutines := 4 chunkSize := fileSize / int64(numGoroutines) ch := make(chan string, numGoroutines) var wg sync.WaitGroup // 启动并发读取 for i := 0; i < numGoroutines; i++ { wg.Add(1) start := int64(i) * chunkSize // 最后一块读取剩余内容 size := chunkSize if i == numGoroutines-1 { size = fileSize - start } go readChunk(filePath, start, size, &wg, ch) } // 等待所有goroutine完成并关闭通道 go func() { wg.Wait() close(ch) }() // 处理读取结果 for chunk := range ch { fmt.Printf("Read chunk: %sn", chunk[:50]) // 打印前50字符示例 }}2. 并发写入文件核心思路:
- 多个 goroutine 向通道发送数据,由单个 goroutine 负责写入文件。
- 使用互斥锁(sync.Mutex)保护文件写入操作(若多个 goroutine 直接写入文件)。
方案一:单写入者模式(推荐)package mainimport ( "fmt" "os" "sync")func writeToFile(filePath string, data <-chan string, wg *sync.WaitGroup) { defer wg.Done() file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) if err != nil { fmt.Println("Error opening file:", err) return } defer file.Close() for line := range data { _, err := file.WriteString(line + "n") if err != nil { fmt.Println("Error writing to file:", err) } }}func main() { filePath := "output.txt" ch := make(chan string, 100) var wg sync.WaitGroup // 启动单个写入goroutine wg.Add(1) go writeToFile(filePath, ch, &wg) // 多个goroutine并发生成数据 for i := 0; i < 10; i++ { go func(id int) { for j := 0; j < 10; j++ { ch <- fmt.Sprintf("Goroutine %d: Line %d", id, j) } }(i) } // 等待所有数据生成完毕并关闭通道 wg.Add(1) go func() { wg.Wait() // 等待所有数据生成goroutine完成 close(ch) }() wg.Done() // 标记写入goroutine已完成 wg.Wait() // 等待写入goroutine结束}方案二:多写入者+互斥锁(性能较低)package mainimport ( "fmt" "os" "sync")func writeWithLock(filePath string, data <-chan string, wg *sync.WaitGroup, mu *sync.Mutex) { defer wg.Done() file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) if err != nil { fmt.Println("Error opening file:", err) return } defer file.Close() for line := range data { mu.Lock() _, err := file.WriteString(line + "n") mu.Unlock() if err != nil { fmt.Println("Error writing to file:", err) } }}func main() { filePath := "output_locked.txt" ch := make(chan string, 100) var wg sync.WaitGroup var mu sync.Mutex // 启动多个写入goroutine for i := 0; i < 3; i++ { wg.Add(1) go writeWithLock(filePath, ch, &wg, &mu) } // 生成数据 go func() { for i := 0; i < 50; i++ { ch <- fmt.Sprintf("Data line %d", i) } close(ch) }() wg.Wait()}关键注意事项并发读取:
分块读取时需确保块之间无重叠或遗漏。
大文件处理建议使用 bufio.Scanner 或内存映射(mmap)。
并发写入:
单写入者模式(推荐):通过通道串行化写入,避免锁竞争。
多写入者模式:必须使用互斥锁,但性能较差,仅适用于低频写入。
资源管理:
使用 defer 确保文件和锁的正确释放。
通过 sync.WaitGroup 同步 goroutine 生命周期。
通过上述方法,可以高效实现 Golang 中的文件并发读写,同时保证数据一致性和程序稳定性。