并发示例混淆

时间:2018-06-11 20:43:49

标签: go concurrency

我正在使用Go更深入地研究并发性。我正在阅读的一本书给出了以下示例,它基本上压缩了命令行参数中的文件。

 package main

 import (
  "compress/gzip"
  "io"
  "os"
 )

 func main() {

     for _, file := range os.Args[1:] {
        compress(file)
     }

 }


 func compress(filename string) error {

     in, err := os.Open(filename)

     if err != nil {
      return err
     }

     defer in.Close()

     out, err := os.Create(filename + ".gz")

     if err != nil {
       return err
     }

     defer out.Close()

     gzout := gzip.NewWriter(out)

     _, err = io.Copy(gzout, in)

     gzout.Close()

     return err
}

这本书然后解释说,如果你想处理几百个文件,保持这样的肯定会比你使用goroutine慢,所以对main()函数进行了以下修改使用它们:

var wg sync.WaitGroup 
var i int = -1
var file string
for i, file = range os.Args[1:] {
    wg.Add(1)
    go func(filename string) {
       compress(filename)
       wg.Done()
     }(file)
 }
 wg.Wait()
 fmt.Printf("Compressed %d files\n", i+1)

然后注意到关于内联函数定义及其参数(文件名)的“技巧”是必要的“因为我们在for循环中执行goroutine”。

我想我不明白为什么需要上面的内联函数来实现这个功能,不能使用以下内容,或者我错过了一个技巧?

for i, file = range os.Args[1:] {
    wg.Add(1)

    go compress(file)

    wg.Done()

 }
 wg.Wait()
 fmt.Printf("Compressed %d files\n", i+1)

1 个答案:

答案 0 :(得分:4)

如果您只是执行:

compress(file)

在同一个goroutine中执行,所以在调用完成之前,你不会做任何其他事情。

因此,如果您想并行处理,则需要使用go关键字启动新的goroutine。

如果你这样做:

go compress(file)

然后它将启动一个新的goroutine来压缩每个文件。

但是,只要main函数启动所有goroutine,它就会结束执行(它不会等待所有goroutine完成)。

这就是为什么他们包含了wg来电。

由于compress函数没有收到WaitGroup来调用Done,因此他们实现了内联函数,以便在goroutine结束后立即调用wg.Done()对该文件的压缩。

原因如下:

go func(filename string) {
    compress(filename)
    wg.Done()
}(file)