Go协程、通道和WaitGroup的输出结果不符合预期。

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

GoRoutines, Channels with WaitGroup unexpected output

问题

我看了一些我很久以前写的代码,当时可能是在 go1.3 发布的时候(我可能记错了)。下面的代码以前能够按预期工作,但是现在,自从我将 go 更新到当前的主版本(go version devel +bd1efd5 Fri Jul 31 16:11:21 2015 +0000 darwin/amd64)之后,最后的输出消息 c <- "FUNC 1 DONE" 没有打印出来,在 play.golang.org 上的代码按预期工作。我做错了什么,还是这是一个 bug?

package main

import (
	"fmt"
	"sync"
	"time"
)

func test(c chan string, wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Println("EXEC FUNC 1")
	time.Sleep(3 * time.Second)
	c <- "FUNC 1 DONE"
}

func test1(c chan string, wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Println("EXEC FUNC 2")
	time.Sleep(2 * time.Second)
	c <- "FUNC 2 DONE"
}

func main() {
	ch := make(chan string)

	var wg sync.WaitGroup
	wg.Add(2)

	go test(ch, &wg)
	go test1(ch, &wg)

	go func(c chan string) {
		for txt := range c {
			fmt.Println(txt)
		}
	}(ch)

	wg.Wait()
}

更新:

我并不是说上面的代码是做这些工作的最佳方式,但是我没有看出任何问题。

同时在 go version go1.4.2 darwin/amd64 上运行将返回预期的输出。

英文:

I looked at some code that I've wrote a long time ago, when go1.3 was released(I might be wrong). CODE HERE

The below code used to work as expected, but now since I've update go to the current master version(go version devel +bd1efd5 Fri Jul 31 16:11:21 2015 +0000 darwin/amd64), the last output message c &lt;- &quot;FUNC 1 DONE&quot; is not printed, the code works as it should on play.golang.org. Did I do something wrong, or this is a bug?

package main

import (&quot;fmt&quot;;&quot;sync&quot;;&quot;time&quot;)

func test(c chan string, wg *sync.WaitGroup) {
  defer wg.Done()
  fmt.Println(&quot;EXEC FUNC 1&quot;)
  time.Sleep(3 * time.Second)
  c &lt;- &quot;FUNC 1 DONE&quot;
}

func test1(c chan string, wg *sync.WaitGroup) {
  defer wg.Done()
  fmt.Println(&quot;EXEC FUNC 2&quot;)
  time.Sleep(2 * time.Second)
  c &lt;- &quot;FUNC 2 DONE&quot;
}

func main() {
  ch := make(chan string)

  var wg sync.WaitGroup
  wg.Add(2)

  go test(ch, &amp;wg)
  go test1(ch, &amp;wg)

  go func(c chan string) {
    for txt := range c {
	  fmt.Println(txt)
	}
  }(ch)

  wg.Wait()
}

UPDATE:

I'm not saying that, the above is the best way of doing those types of work, but I don't see anything wrong with it.

Also running it in go version go1.4.2 darwin/amd64 will return the expected output.

答案1

得分: 7

你的代码一直存在这个 bug。只是偶然间你的程序在 main 函数退出之前成功打印了所有的消息。

为了正确地解决这个问题,我建议你将 wg.Wait() 和通道接收的位置互换,这样你就可以异步关闭通道。这样一来,接收操作将阻塞 main 函数,而通道会在所有发送操作完成后立即关闭。

func main() {
    ch := make(chan string)

    var wg sync.WaitGroup
    wg.Add(2)

    go test(ch, &wg)
    go test1(ch, &wg)

    go func() {
        wg.Wait()
        close(ch)
    }()

    for txt := range ch {
        fmt.Println(txt)
    }
}

希望对你有帮助!

英文:

Your code has always had this bug. It was only by chance that your program managed to print all messages before main exited.

To make this work correctly, I would invert where you have the wg.Wait() and the channel receives, so you can asynchronously close the channel. This way the receive operations are what is blocking main, and the channel is closed as soon as all send operations are done.

func main() {
	ch := make(chan string)

	var wg sync.WaitGroup
	wg.Add(2)

	go test(ch, &amp;wg)
	go test1(ch, &amp;wg)

	go func() {
		wg.Wait()
		close(ch)
	}()

	for txt := range ch {
		fmt.Println(txt)
	}
}

huangapple
  • 本文由 发表于 2015年8月11日 22:59:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/31945315.html
匿名

发表评论

匿名网友

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

确定