【问题标题】:waiting for all go routines to finish等待所有 goroutine 完成
【发布时间】:2017-10-04 11:37:40
【问题描述】:

第一次使用 go,并尝试让 go 例程和 WaitGroups 正常工作。

我有一个包含 100 行数据的 CSV 文件。 (101 包括标题)

我有以下简单的代码:

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "sync"
    "time"
)

func main() {
    start := time.Now()
    numRows := 0

    waitGroup := sync.WaitGroup{}
    file, _ := os.Open("./data.csv")

    scanner := bufio.NewScanner(file)
    scanner.Scan() // to read the header

    for scanner.Scan() {
        err := scanner.Err()

        if err != nil && err != io.EOF {
            panic(err)
        }

        waitGroup.Add(1)

        go (func() {
            numRows++
            waitGroup.Done()
        })()
    }

    waitGroup.Wait()
    file.Close()

    fmt.Println("Finished parsing ", numRows)
    fmt.Println("Elapsed time in seconds: ", time.Now().Sub(start))
}

当我运行它时,numRows 的输出每次都会在 94 和 100 之间波动。我希望它每次都是100。如果我在 10 行数据的 CSV 上运行相同的代码,它每次都会输出 10

在我看来,最后几个围棋程序没有及时完成。

我尝试了以下失败的方法:

  • 使用CsvReader 而不是Scanner
  • waitGroup.Add(1) 移动到匿名函数下方
  • 将匿名 func 移出到包级作用域 func(并使用 ptrs 传递事物)

我错过了什么?

【问题讨论】:

  • 在比赛检测器下运行它:-)

标签: go concurrency goroutine


【解决方案1】:

在不同的 goroutine 中同时修改单个变量是不安全的。您对numRows 的一些更新将会丢失,有时您的程序可能会崩溃。

要么使用互斥锁保护您的 numRows 变量,要么使用 atomic 函数之一以原子方式进行添加:

var numRows int32

// ...

    go (func() {
        atomic.AddInt32(&numRows, 1)
        waitGroup.Done()
    })()

【讨论】:

  • 现在您已经说过了,这很有道理。我想我现在开始对 goroutines 有了更多的了解。也感谢有关原子函数的提示。
【解决方案2】:

你如何处理这段代码:

for scanner.Scan() {
    err := scanner.Err()

    if err != nil && err != io.EOF {
        panic(err)
    }

    waitGroup.Add(1)

    go (func() {
        numRows++
        waitGroup.Done()
    })()
}

实际上所有工作都在一个主 goroutine 中完成,只有 numRows 增量使用单独的 goroutine。我认为这可以简化为简单的增量:

for scanner.Scan() {
    err := scanner.Err()

    if err != nil && err != io.EOF {
        panic(err)
    }
    numRows++
}

如果您想模拟并行解析和流水线,您可以使用通道。只让一个 goroutine 负责计数器增量。每次当另一个 goroutine 想要增加计数器时,它都会向该通道发送一条消息。

https://play.golang.org/p/W60twJjY8P

【讨论】:

  • 毫无疑问可以简化,但我认为这不是问题的重点。
  • 是的,刚刚分享了一些如何使用通道序列化访问的想法
  • 啊,这是有道理的。我的代码非常脆弱,但关键是我要掌握 go 中的并发性,所以这将我推向了正确的方向。谢谢!
猜你喜欢
  • 2018-02-24
  • 2019-02-01
  • 2014-11-15
  • 2019-01-16
  • 2018-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-27
相关资源
最近更新 更多