【问题标题】:What is the best way to create goroutines dynamically?动态创建 goroutine 的最佳方法是什么?
【发布时间】:2014-11-07 16:25:30
【问题描述】:

对不起,如果这是一个新手问题,但我是新手 ;)。我有following playground。如何动态创建我的 goroutine?我在操场上的第一组按预期工作,但我的第二组为每个值返回“11”。我可以通过取消注释第 38 行来解决它,但这似乎有点像 hack。有没有更喜欢的方式来动态创建我的 goroutine?

package main

import (
    "fmt"
    "log"
    "time"
)

func myFunc(i int) int {
    return i
}

func first() {
    firstChannel := make(chan int)
    go func() { firstChannel <- myFunc(0) }()
    go func() { firstChannel <- myFunc(1) }()
    go func() { firstChannel <- myFunc(2) }()
    go func() { firstChannel <- myFunc(3) }()
    go func() { firstChannel <- myFunc(4) }()
    go func() { firstChannel <- myFunc(5) }()
    go func() { firstChannel <- myFunc(6) }()
    go func() { firstChannel <- myFunc(7) }()
    go func() { firstChannel <- myFunc(8) }()
    go func() { firstChannel <- myFunc(9) }()
    go func() { firstChannel <- myFunc(10) }()

    for k := 0; k < 11; k++ {
        select {
        case result := <-firstChannel:
            log.Println(result)
        }
    }
}
func second() {
    secondChannel := make(chan int)
    for j := 0; j < 11; j++ {
        go func() { secondChannel <- myFunc(j) }()
        //time.Sleep(1*time.Millisecond)
    }
    for k := 0; k < 11; k++ {
        select {
        case result := <-secondChannel:
            log.Println(result)
        }
    }
}

func main() {
    fmt.Println("First set------------------")
    first()
    time.Sleep(1 * time.Second)
    fmt.Println("Second set------------------")
    second()
}

结果:

First set------------------
2009/11/10 23:00:00 0
2009/11/10 23:00:00 1
2009/11/10 23:00:00 2
2009/11/10 23:00:00 3
2009/11/10 23:00:00 4
2009/11/10 23:00:00 5
2009/11/10 23:00:00 6
2009/11/10 23:00:00 7
2009/11/10 23:00:00 8
2009/11/10 23:00:00 9
2009/11/10 23:00:00 10
Second set------------------
2009/11/10 23:00:01 11
2009/11/10 23:00:01 11
2009/11/10 23:00:01 11
2009/11/10 23:00:01 11
2009/11/10 23:00:01 11
2009/11/10 23:00:01 11
2009/11/10 23:00:01 11
2009/11/10 23:00:01 11
2009/11/10 23:00:01 11
2009/11/10 23:00:01 11
2009/11/10 23:00:01 11

【问题讨论】:

    标签: go


    【解决方案1】:

    用循环

    for j := 0; j < 11; j++ {
        go func() { secondChannel <- myFunc(j) }()
        //time.Sleep(1*time.Millisecond)
    }
    

    您创建了一个闭包,j 变量将在您的 second() 函数和您在循环中创建的在单独的 go 例程中运行的函数之间共享。(参见例如 here) - 所以存在竞争条件当引用 j 变量时。

    go 例程可能不会在循环结束之前开始执行,然后所有的 j 变量都会在循环结束时看到。

    你可以通过让每个 goroutine 引用一个单独的变量来解决这个问题:

    for j := 0; j < 11; j++ {
        arg := j
        go func() { secondChannel <- myFunc(arg) }()
        //time.Sleep(1*time.Millisecond)
    }
    

    现在 func() 在循环中创建的闭包引用了arg 变量,每次迭代都是分开的。

    或者您可以通过将变量复制到 func() 的参数来传递它:

    for j := 0; j < 11; j++ {
        go func(arg int) { secondChannel <- myFunc(arg) }(j)
        //time.Sleep(1*time.Millisecond)
    }
    

    【讨论】:

    • 我真的很讨厌必须在两个非常可靠的答案之间做出选择,所以我完全基于时间......看起来你被@PeterSO 稍微击败了。我给你投了赞成票。谢谢!
    • 我喜欢将它作为 arg 传递,因为它清楚地表明内部变量与 func 文字相关,而不是让读者看到一个潜在的神秘声明。
    【解决方案2】:

    The Go Programming Language Specification

    Function literals

    函数字面量是闭包:它们可以引用定义在 一个周边功能。然后这些变量在 周围的函数和函数字面量,它们作为 只要它们可以访问。

    What happens with closures running as goroutines?

    在并发使用闭包时可能会出现一些混淆。

    要在启动时将 v 的当前值绑定到每个闭包,一个 必须修改内部循环以在每次迭代中创建一个新变量。 一种方法是将变量作为参数传递给闭包。

    更简单的方法是使用声明创建一个新变量 看起来很奇怪但在 Go 中运行良好的风格。

    Captured Closure (for Loop Variable) in Go

    使用j := j。例如,

    package main
    
    import (
        "fmt"
        "log"
        "time"
    )
    
    func myFunc(i int) int {
        return i
    }
    
    func first() {
        firstChannel := make(chan int)
        go func() { firstChannel <- myFunc(0) }()
        go func() { firstChannel <- myFunc(1) }()
        go func() { firstChannel <- myFunc(2) }()
        go func() { firstChannel <- myFunc(3) }()
        go func() { firstChannel <- myFunc(4) }()
        go func() { firstChannel <- myFunc(5) }()
        go func() { firstChannel <- myFunc(6) }()
        go func() { firstChannel <- myFunc(7) }()
        go func() { firstChannel <- myFunc(8) }()
        go func() { firstChannel <- myFunc(9) }()
        go func() { firstChannel <- myFunc(10) }()
    
        for k := 0; k < 11; k++ {
            select {
            case result := <-firstChannel:
                log.Println(result)
            }
        }
    }
    func second() {
        secondChannel := make(chan int)
        for j := 0; j < 11; j++ {
            j := j
            go func() { secondChannel <- myFunc(j) }()
            //time.Sleep(1*time.Millisecond)
        }
        for k := 0; k < 11; k++ {
            select {
            case result := <-secondChannel:
                log.Println(result)
            }
        }
    }
    
    func main() {
        fmt.Println("First set------------------")
        first()
        time.Sleep(1 * time.Second)
        fmt.Println("Second set------------------")
        second()
    }
    

    输出:

    第一组------ 2009/11/10 23:00:00 0 2009/11/10 23:00:00 1 2009/11/10 23:00:00 2 2009/11/10 23:00:00 3 2009/11/10 23:00:00 4 2009/11/10 23:00:00 5 2009/11/10 23:00:00 6 2009/11/10 23:00:00 7 2009/11/10 23:00:00 8 2009/11/10 23:00:00 9 2009/11/10 23:00:00 10 第二盘------ 2009/11/10 23:00:01 0 2009/11/10 23:00:01 1 2009/11/10 23:00:01 2 2009/11/10 23:00:01 3 2009/11/10 23:00:01 4 2009/11/10 23:00:01 5 2009/11/10 23:00:01 6 2009/11/10 23:00:01 7 2009/11/10 23:00:01 8 2009/11/10 23:00:01 9 2009/11/10 23:00:01 1

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-12
      • 2015-06-14
      • 2017-08-06
      • 2013-03-26
      • 1970-01-01
      • 1970-01-01
      • 2022-01-05
      • 2017-04-13
      相关资源
      最近更新 更多