为什么 Go 语言的 goroutine 函数从未被调用?

huangapple go评论74阅读模式
英文:

go lang, why go routine function never being called

问题

以下是翻译好的代码:

package main

import (
    "fmt"
    "runtime"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        //runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    go say("world") // 创建一个新的goroutine
    runtime.Gosched()

    say("hello") // 当前的goroutine
}

为什么结果是:

hello
hello
hello
hello
hello

为什么没有输出world

回答:
如果我这样修改代码,就可以输出world了:

package main

import (
    "fmt"
    "runtime"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        //runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    go say("world") // 创建一个新的goroutine
    runtime.Gosched()

    say("hello") // 当前的goroutine
}
英文:
package main

import (
    &quot;fmt&quot;
    //&quot;runtime&quot;
)

func say(s string) {
    for i := 0; i &lt; 5; i++ {
        //runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    go say(&quot;world&quot;) // create a new goroutine
    say(&quot;hello&quot;) // current goroutine
}

Why result is:

> hello
> hello
> hello
> hello
> hello

Why is there no world?

Answer: (edited:)
If I do this, it is good now:

package main

import (
    &quot;fmt&quot;
    &quot;runtime&quot;
)

func say(s string) {
    for i := 0; i &lt; 5; i++ {
        //runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    go say(&quot;world&quot;) // create a new goroutine
    runtime.Gosched()

    say(&quot;hello&quot;) // current goroutine
}

答案1

得分: 2

你只是遇到了一个时间问题,因为你没有“协调”你的go程。处理这个问题的常见方法是使用等待组(wait group)。另一种选择是使用通道和阻塞选择(blocking select)。一个使用等待组的实现可能如下所示:

func main() {
    wg := sync.WaitGroup{}
    wg.Add(1)
    go say("world") // 创建一个新的goroutine

    wg.Add(1)
    say("hello") // 当前的goroutine
    wg.Wait()
}

而通道选项(在这个例子中实际上并不有趣或有用)可能是这样的:

func main() {
    done := make(chan bool)
    go say("world", done) // 创建一个新的goroutine

    say("hello", done) // 当前的goroutine

    select {
    case fin := <-done:
        // 完成时的操作
        return
    }
}

func say(s string, done chan bool) {
    for i := 0; i < 5; i++ {
        fmt.Println(s)
    }
    done <- true
}

然而,上面的例子中,第一个调用say完成后,程序就会结束执行。要确保两个都完成,你需要给它们传递不同的通道,并在两个通道上进行阻塞读取。这更像是一种模式,当你的go程正在做实际工作,并且你想要从它们那里获取数据或需要更复杂的协调(例如基于先前结果生成新的goroutine等)时,我会使用这种模式。

英文:

You're just experiencing a timing issue because you're not 'coordinating' your go routines. The common way to handle this is with a wait guard. The other option I see from time to time using channels and a blocking select. A wait guard implementation would look something like this;

func main() {
    wg := sync.WaitGroup{}
    wg.Add(1)
    go say(&quot;world&quot;) // create a new goroutine

    wg.Add(1)
    say(&quot;hello&quot;) // current goroutine
    wg.Wait()
}

While the channel option (not actually interesting or useful in this example) is something more like this;

 func main() {
    done := make(chan bool)
    go say(&quot;world&quot;, done) // create a new goroutine

    say(&quot;hello&quot;, done) // current goroutine
    
    select {
        case fin := &lt;- done:
           //on finished
           return
    }
}

func say(s string, chan bool) {
for i := 0; i &lt; 5; i++ {
    fmt.Println(s)
   }
   done &lt;- true
}

With the example above though, the first call to say completing would allow the program to finish executing. To ensure both finish you'd have to pass different channels to each and have blocking reads on both. This is more a pattern I would use when your go routines are doing real work and you want to bring data to aggregate data from them or need more complex coordination (like spawning new goroutines based on the results of previous ones ect).

答案2

得分: 1

你没有允许goroutine在main()退出之前运行。

尽管对say的第二次调用会阻塞(短暂),但不能保证第一个goroutine能够运行。你需要等待两个goroutine都返回,通常可以使用WaitGroup来实现。

var wg sync.WaitGroup

func say(s string) {
    defer wg.Done()
    for i := 0; i < 5; i++ {
        //runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    wg.Add(1)
    go say("world") // 创建一个新的goroutine

    wg.Add(1)
    say("hello") // 当前的goroutine
    wg.Wait()
}
英文:

You're not allowing the goroutine to run before main() exits.

Even though the second call to say blocks (briefly), there's no guarantee that the first goroutine can run. You need to wait for both to return, which is often done with a WaitGroup

var wg sync.WaitGroup

func say(s string) {
	defer wg.Done()
	for i := 0; i &lt; 5; i++ {
		//runtime.Gosched()
		fmt.Println(s)
	}
}

func main() {
	wg.Add(1)
	go say(&quot;world&quot;) // create a new goroutine
    
	wg.Add(1)
	say(&quot;hello&quot;) // current goroutine
	wg.Wait()
}

答案3

得分: 0

这是因为主 goroutine 退出得太早了。当主 goroutine 退出时,进程也会退出,所以其他 goroutine 没有机会运行。如果你希望 goroutine 运行,就要保持主程序足够长的时间。

package main

import (
    "fmt"
    "runtime"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        fmt.Println(s)
    }
}

func main() {
    go say("world") // 创建一个新的 goroutine
    runtime.Gosched()

    say("hello") // 当前 goroutine
    time.Sleep(1 * time.Second) // 这一行
}
英文:

It's because the main goroutine exits too early. When the main goroutine exits, the process will exit, so there is no chance for other goroutines to run. If you wish the goroutines to run, keep the main routine alive long enough.

package main

import (
    &quot;fmt&quot;
    &quot;runtime&quot;
)

func say(s string) {
    for i := 0; i &lt; 5; i++ {
        fmt.Println(s)
    }
}

func main() {
    go say(&quot;world&quot;) // create a new goroutine
    runtime.Gosched()

    say(&quot;hello&quot;) // current goroutine
    time.Sleep(1 * time.Second) // this line
}

huangapple
  • 本文由 发表于 2015年4月15日 23:26:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/29654328.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定