Golang高效创建数千个文件的方法
Golang高效创建数千个文件的方法
我开始学习Go协程,对于并发和并行感到有些困惑,因此我认为掌握它们的最佳方式是开始编写一些展示其用法的小范例。基于此,我正在考虑编写一个程序,在我的硬盘上创建一个空文件(例如 touch file)。
- 在运行于多线程CPU硬件的Go语言中,实现此功能最快的方法是什么?
- 我真的需要使用通道吗?我认为不需要,只需要等待组就足够了,因为我只想确保在所有Go协程完成其工作之前,main()函数不会返回。
- 应该并行运行多少个Go协程?由于我只有8个逻辑CPU核心可用,我猜一次只能并行运行8个Go协程。这是否意味着我必须将工作分成每个Go协程处理8个文件的块?
- 我该如何对此进行基准测试,以便得出自己的结论?
更多关于Golang高效创建数千个文件的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你好 @skillian,感谢你的回复 🙂 确实,我在 ext3 和 BTRFS 这两种不同的文件系统上运行代码时看到了不同的数字。关于文件构造函数,你有什么建议或经验吗?我还在考虑创建一个针对所有 TCP 端口的端口扫描器,为此系统需要创建一个套接字,这在我的 Linux 磁盘上也是一个文件——你认为单个 Go 协程会比多个协程性能更好吗?
我还有一个问题:既然我有 6 个核心(超线程下是 12 个),那么对我来说,启动超过 12 个 Go 协程来并行处理一个函数,这究竟有意义吗?
更多关于Golang高效创建数千个文件的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你好,@drpaneas,如果你是在单个文件系统的单个目录中创建这些空文件,那么我怀疑使用多核相比单核不会带来任何性能提升。我估计使用单个 goroutine 会获得最佳性能。空文件不需要向磁盘写入数据,只需要写入文件表/inode 树(取决于文件系统)。如果你将这些文件放入不同的目录,某些文件系统驱动程序可能能够处理对磁盘上这些目录的并发访问,那么每个目录使用一个 goroutine 可能会带来好处。即便如此,顺序磁盘访问通常也比随机访问快,所以使用单个 goroutine 可能仍然更好。
我建议使用 testing 包的 B 类型实现一些基准测试,以比较调整不同变量时的性能。
在以下情况下,你很可能也会得到非常不同的性能特征:
- 如果文件非空
- 如果你更改写入的文件系统,例如,即使只是从 ext2 更改为 ext3。
- 固态硬盘和机械硬盘之间:机械硬盘可能更慢,但可能从并发中受益。如果你处理的是非空文件,这一点可能更明显,因为操作系统可以重新组织对机械硬盘的读写操作。
- 基于计算机和存储之间的硬件和/或软件接口(例如 SATA、SAS、NAS、云存储)。
在Go中高效创建数千个文件,可以利用并发和并行处理。以下是一个示例实现,使用sync.WaitGroup来等待所有goroutine完成,并通过限制并发数避免资源耗尽。
package main
import (
"fmt"
"os"
"path/filepath"
"sync"
"time"
)
func createFiles(workerID int, filePaths <-chan string, wg *sync.WaitGroup) {
defer wg.Done()
for path := range filePaths {
file, err := os.Create(path)
if err != nil {
fmt.Printf("Worker %d: Failed to create %s: %v\n", workerID, path, err)
continue
}
file.Close()
}
}
func main() {
start := time.Now()
numFiles := 5000
baseDir := "./test_files"
os.MkdirAll(baseDir, 0755)
// 创建文件路径通道
filePaths := make(chan string, numFiles)
// 生成文件路径
go func() {
for i := 0; i < numFiles; i++ {
filePaths <- filepath.Join(baseDir, fmt.Sprintf("file_%d.txt", i))
}
close(filePaths)
}()
// 根据CPU核心数设置worker数量
numWorkers := 8
var wg sync.WaitGroup
wg.Add(numWorkers)
// 启动worker
for i := 0; i < numWorkers; i++ {
go createFiles(i, filePaths, &wg)
}
wg.Wait()
elapsed := time.Since(start)
fmt.Printf("Created %d files in %v\n", numFiles, elapsed)
}
对于基准测试,可以使用Go的内置测试框架。创建一个benchmark_test.go文件:
package main
import (
"testing"
)
func BenchmarkCreateFiles(b *testing.B) {
for i := 0; i < b.N; i++ {
// 调用你的文件创建函数
// 确保每次测试使用不同的目录
}
}
运行基准测试:
go test -bench=. -benchtime=10s
这个实现使用了工作池模式,通过通道分发文件路径,每个worker goroutine从通道中获取路径并创建文件。worker数量设置为逻辑CPU核心数(示例中为8),这允许并行执行,同时避免创建过多goroutine导致的调度开销。
通道在这里用于任务分发,而sync.WaitGroup确保主函数等待所有worker完成。这种组合在并发文件创建中很常见,既实现了并行处理,又提供了同步机制。

