使用goroutine复制子目录

时间:2014-01-30 14:40:36

标签: go

我的程序将多个文件和目录从计算机的不同部分复制到一个地方。

其中一个目录非常大,因此复制它需要大约20-30秒。现在我只是制作了这个方法,将该目录复制为goroutine:

func CopySpecificDirectory(source, dest string, quit chan int) (err error) {
    files, err := os.Open(source)
    file, err := files.Readdir(0)

    if err != nil {
        fmt.Printf("Error reading directory %s: %s\n", source, err)
        return err
    }

    for _, f := range file {
        if f.IsDir() {
            copy.CopyDir(source+"\\"+f.Name(), dest+"\\"+f.Name())
        } else {
            copy.CopyFile(source+"\\"+f.Name(), dest+"\\"+f.Name())
        }
    }

    quit <- 1

    return nil
}

主要:

quit := make(chan int)
go CopySpecificDirectory(config.Location+"\\Directory", config.Destination, quit)

这只会让我的程序提高几秒钟。在我的CopySpecificDirectory方法中(如果这是最好的方法)我想为每个目录创建一个goroutine,这样的事情可能:

c := make(chan int)
for _, f := range file {
    if f.IsDir() {
        go func() {
            copy.CopyDir(source+"\\"+f.Name(), dest+"\\"+f.Name())
            c <- 1
        }()
    } else {
        copy.CopyFile(source+"\\"+f.Name(), dest+"\\"+f.Name())
    }
}

通过这种方法,我不知道在哪里等待副本完成每个目录(&lt; -c)。
这是最好的方法吗?如果有人有其他建议什么是复制目录的最快方法,我很乐意听到它。

编辑:

我使用了来自网站的sync.WaitGroup示例的aproach。

for _, f := range file {
    if f.IsDir() {
        wg.Add(1)
        go func() {
            defer wg.Done()
            copy.CopyDir(source+"\\"+f.Name(), dest+"\\"+f.Name())
        }()
    // more code

我已将var wg sync.WaitGroup声明为全球,我在致电wg.Wait()后在主要权利CopySpecificDirectory

CopySpecificDirectory在复制所有内容之前完成。我究竟做错了什么 ?看起来它不是在等待goroutines完成。

3 个答案:

答案 0 :(得分:1)

  

使用这种方法,我不知道在哪里等待副本完成每个目录(&lt; -c)。

您可以使用SyncGroup来协调所有goroutine,而不是在频道上发送信号。您为每个衍生的goroutine致电wg.Add(1),并在他们完成时致电wg.Done()。然后你在产生所有这些之后做wg.Wait()等待它们全部完成。

至于如何加速复制一般,没有明确的答案。这取决于很多因素(可能是操作系统,文件系统,硬盘,负载等)。

答案 1 :(得分:1)

使用sync.WaitGroup()代替频道:

  1. 创建一个等待组对象。
  2. 在生成goroutine之前,Add()生成一个。
  3. 当goroutine即将退出时,它会在该对象上调用Done()
  4. 在您的主(等待)代码中,在该对象上调用Wait()。一旦所有goroutine以这种方式“跟踪”完成执行,该函数将返回。

  5. 请注意,您的程序受I / O限制,而不受CPU限制。如果您的代码需要将文件从物理上不同的设备复制到(其他)物理上不同的设备,那么可以节省一些时间。如果您只是在同一个文件系统上浏览文件,或者您的所有源都在同一个文件系统上,或者您的所有目标位于同一个文件系统上,那么您将无法获得太多收益,因为您的goroutine只会在单个共享资源上竞争 - 存储设备 - 最终结果与您只是按顺序执行复制操作的情况没有太大区别。

    为了提供示例,/etc/fstab文件的手册页包含有关经典Unix系统上已安装/可安装文件系统的信息,提到操作系统从不同时检查位于同一物理介质上的文件系统顺序地,它同时检查位于不同驱动器上的文件系统。请参阅manual page中的fs_passno参数条目。

答案 2 :(得分:0)

感谢@kostix和@justinas的帮助。我按照他们的解决方案,唯一的问题仍然是我的for循环f内部没有必然绑定,直到循环完成。

所以我必须添加f := f。这现在有效:

for _, f := range file {
    f := f
    if f.IsDir() {
        wg.Add(1)
        go func() {
            copy.CopyDir(source+"\\"+f.Name(), dest+"\\"+f.Name())
            defer wg.Done()
        }()
    } else {
        copy.CopyFile(source+"\\"+f.Name(), dest+"\\"+f.Name())
    }
}