Go中没有来自goroutine的输出

时间:2015-03-10 07:28:47

标签: go concurrency goroutine

SayHello()按预期执行时,goroutine不会打印任何内容。

package main

import "fmt"

func SayHello() {
    for i := 0; i < 10 ; i++ {
        fmt.Print(i, " ")
    }
}

func main() {
    SayHello()
    go SayHello()
}

3 个答案:

答案 0 :(得分:31)

当您的main()功能结束时,您的计划也会结束。它不会等待其他goroutines完成。

引用Go Language Specification: Program Execution

  

程序执行从初始化主包然后调用函数main开始。当该函数调用返回时,程序退出。它不会等待其他(非main)goroutines完成。

有关详细信息,请参阅this answer

你必须告诉你的main()函数等待SayHello()函数作为goroutine完成。您可以将它们与频道同步,例如:

func SayHello(done chan int) {
    for i := 0; i < 10; i++ {
        fmt.Print(i, " ")
    }
    if done != nil {
        done <- 0 // Signal that we're done
    }
}

func main() {
    SayHello(nil) // Passing nil: we don't want notification here
    done := make(chan int)
    go SayHello(done)
    <-done // Wait until done signal arrives
}

另一种方法是通过关闭频道来表示完成:

func SayHello(done chan struct{}) {
    for i := 0; i < 10; i++ {
        fmt.Print(i, " ")
    }
    if done != nil {
        close(done) // Signal that we're done
    }
}

func main() {
    SayHello(nil) // Passing nil: we don't want notification here
    done := make(chan struct{})
    go SayHello(done)
    <-done // A receive from a closed channel returns the zero value immediately
}

备注:

根据您的编辑/评论:如果您希望2个正在运行的SayHello()功能打印&#34;混合&#34;数字随机:你无法保证观察到这种行为。再次,请参阅aforementioned answer了解更多详情。 Go Memory Model仅保证某些事件在其他事件发生之前发生,您无法保证如何执行2个并发的goroutine。

您可以尝试使用它,但要知道结果不是确定性的。首先,您必须使用以下命令启用多个活动goroutine:

runtime.GOMAXPROCS(2)

第二,你必须首先启动SayHello()作为goroutine,因为你当前的代码首先在主goroutine中执行SayHello(),只有在完成后才启动另一个:

runtime.GOMAXPROCS(2)
done := make(chan struct{})
go SayHello(done) // FIRST START goroutine
SayHello(nil) // And then call SayHello() in the main goroutine
<-done // Wait for completion

答案 1 :(得分:10)

或者(对于icza的回答),您可以使用WaitGroup包和匿名函数中的sync来避免更改原始SayHello

package main

import (
    "fmt"
    "sync"
)

func SayHello() {
    for i := 0; i < 10; i++ {
        fmt.Print(i, " ")
    }
}

func main() {
    SayHello()

    var wg sync.WaitGroup
    wg.Add(1)

    go func() {
        defer wg.Done()
        SayHello()
    }()

    wg.Wait()
}

为了同时打印数字,请在单独的例程中运行每个print语句,如下所示

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(fnScopeI int) {
            defer wg.Done()

            // next two strings are here just to show routines work simultaneously
            amt := time.Duration(rand.Intn(250))
            time.Sleep(time.Millisecond * amt)

            fmt.Print(fnScopeI, " ")
        }(i)
    }

    wg.Wait()
}

答案 2 :(得分:0)

就像其他人提到的那样,当main函数返回时,Go程序退出。

一种选择是使用sync.WaitGroup之类的东西来等待main产生的其他goroutine,然后再从main返回。

另一种选择是在runtime.Goexit()中调用main。来自godoc

  

Goexit终止调用它的goroutine。没有其他goroutine受到影响。 Goexit在终止goroutine之前运行所有延迟的调用。由于Goexit并非恐慌,因此这些延迟函数中的任何恢复调用都将返回nil。

     

从主goroutine调用Goexit会终止该goroutine,而不会返回func main。由于func main尚未返回,因此程序将继续执行其他goroutine。如果所有其他goroutine退出,程序将崩溃。

这允许主goroutine在后台例程继续执行时停止执行。例如:

package main

import (
    "fmt"
    "runtime"
    "time"
)

func f() {
    for i := 0; ; i++ {
        fmt.Println(i)
        time.Sleep(10 * time.Millisecond)
    }
}

func main() {
    go f()
    runtime.Goexit()
}

这比永久阻塞主函数更干净,尤其是对于无限大的程序。缺点是,如果某个流程的所有goroutine都返回或退出(包括主goroutine),则Go会将其检测为错误并感到恐慌:

fatal error: no goroutines (main called runtime.Goexit) - deadlock!

为避免这种情况,至少一个goroutine必须在返回之前调用os.Exit。调用os.Exit(0)会立即终止程序,并指示它这样做没有错误。例如:

package main

import (
    "fmt"
    "os"
    "runtime"
    "time"
)

func f() {
    for i := 0; i < 10; i++ {
        fmt.Println(i)
        time.Sleep(10 * time.Millisecond)
    }
    os.Exit(0)
}

func main() {
    go f()
    runtime.Goexit()
}