如何使用Golang实现多线程文件读写_Golang goroutine与io操作实践

根本原因是高并发下争抢文件描述符、磁盘I/O队列和内核缓冲区;建议用带缓冲channel限制并发数,如sem := make(chan struct{}, 10)。

如何使用golang实现多线程文件读写_golang goroutine与io操作实践

goroutine 并发读文件时为什么反而变慢?

直接用 go readFile(filename) 启动几十个 goroutine 读大文件,常出现整体耗时比串行还长。根本原因不是 goroutine 本身慢,而是底层 os.Openio.Read 在高并发下争抢系统文件描述符、磁盘 I/O 队列和内核缓冲区,尤其在机械硬盘或 NFS 上更明显。

实操建议:

  • 限制并发数:用带缓冲的 channel 控制活跃 goroutine 数量,例如 sem := make(chan struct{}, 10),每次读前 sem ,读完后
  • 避免重复打开同一文件:若多个 goroutine 需读同一文件,先 os.Open 一次,传入 *os.File,而非反复调用 os.ReadFile
  • 对小文件(os.ReadFile + goroutine 即可;大文件务必用 bufio.NewReader 分块读,减少系统调用次数

如何安全地并发写同一个文件?

多个 goroutine 直接 f.Write() 到同一个 *os.File 会引发数据错乱——Go 的 Write 不是原子操作,底层 write(2) 调用可能被调度打断。即使加了 mutex,也无法保证写入顺序和偏移位置正确。

正确做法分场景:

立即学习go语言免费学习笔记(深入)”;

绘蛙AI商品图

绘蛙AI商品图

电商场景的AI创作平台,无需高薪聘请商拍和文案团队,使用绘蛙即可低成本、批量创作优质的商拍图、种草文案

下载

  • 追加写(append):用 os.O_APPEND 标志打开文件,此时内核保证每次 write(2) 原子性追加,配合 sync.Mutex 保护写内容构造逻辑即可
  • 随机写 / 覆盖写:必须由单个 goroutine 统一处理写请求,其他 goroutine 通过 channel 发送 struct{ offset int64; data []byte } 到 writer goroutine
  • 写不同文件:最简单,每个 goroutine 独占一个 *os.File,无需同步
file, _ := os.OpenFile("out.bin", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
defer file.Close()
var mu sync.Mutex
go func() {
    mu.Lock()
    file.Write([]byte("log line/n"))
    mu.Unlock()
}()

bufio.Reader/Writer 在 goroutine 中要注意什么?

bufio.Readerbufio.Writer **不是并发安全的**。它们内部缓存数据并维护读写位置,多个 goroutine 同时调用 ReadStringWriteString 会导致 panic 或数据污染。

使用前提:

  • 每个 goroutine 必须持有自己独立的 bufio.Readerbufio.Writer 实例
  • 不能把同一个 bufio.Reader 传给多个 goroutine,哪怕只读也不行(因为 Read 会移动内部 r.bufr.r
  • 对网络连接(如 net.Conn)做并发读写时,应拆成两个 goroutine:一个专读(配独立 bufio.Reader),一个专写(配独立 bufio.Writer

为什么 defer file.Close() 在 goroutine 里容易漏掉?

典型错误:

go func(name string) {
    f, _ := os.Open(name)
    defer f.Close() // 这行永远不会执行!
    // ... 处理逻辑
}("data.txt")

因为 goroutine 启动后立即返回,外层函数结束,但该 goroutine 可能还没运行到 defer 行就因 panic 或提前 return 退出,defer 不触发。

可靠写法:

  • Close() 放在业务逻辑末尾,显式调用
  • errgroup.Group 管理 goroutine 生命周期,确保所有 goroutine 结束后统一 Close
  • 对临时文件,用 os.CreateTemp + defer os.Remove 配合显式 Close

真正难处理的是「写一半出错」的情况——必须检查 Writer.Flush()Close() 的返回值,否则缓存中的数据可能静默丢失。

https://www.php.cn/faq/2034070.html

发表回复

Your email address will not be published. Required fields are marked *