goroutine的defer在主作用域结束时没有被调用。

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

goroutine's defer not called when main scope ends

问题

包含的代码是一个Go语言程序,它的输出是:1 2 3 -1。但是我本来以为goroutine的defer语句会被调用,输出应该是:1 2 3 -2 -1。

在我的实际代码中,我的goroutine被一个websocket阻塞了...我想我可以发送一个关闭信号,但我还没有弄清楚如何做到等待多个对象(如果在Go中确实可以这样做的话)。我目前通过将deferred的-2移到主作用域来解决我的问题。

我是否有什么关于defer语句放置的技巧没有做对?

英文:
package main

import(
	"fmt"
	"time"
)

func main(){
	fmt.Println("1")
	defer fmt.Println("-1")
	go func() { 
		fmt.Println("2")
		defer fmt.Println("-2")
		time.Sleep(9 * time.Second)
	}()
	time.Sleep(1 * time.Second)
	fmt.Println("3")
}

Produces the output: 1 2 3 -1
But I would've thought the goroutine's defer would have been called to produce: 1 2 3 -2 -1

In my actual code my goroutine is blocked on a websocket... I suppose I could send a shutdown signal but I haven't yet figured out how to do a wait-on-multiple-objects kind of thing (if it can actually be done in go). I'm currently solving my problem by hoisting the deferred -2 into the main scope.

Is there some trick to defer placement I'm not doing right?

http://play.golang.org/p/qv8UEuF2Rb

答案1

得分: 7

(我没有足够的声望来评论?)

是的,问题出在goroutine的调度上,而且与平台有关。在goroutine的函数末尾使用runtime.Gosched可以确保它获得一些CPU时间。目前在x86_64 linux上,下面的代码将输出"3",但如果没有使用Gosched,将输出"1":

不,那不是问题所在,而且这只是通过巧合解决了它(通过实现细节)。正如在https://stackoverflow.com/questions/19613931/goroutines-defer-not-called-when-main-scope-ends/19614010#19614010中已经提到的那样。你的代码存在竞态条件,因此你必须显式同步goroutines。最简单的方法是使用通道或waitgroup http://golang.org/pkg/sync/#WaitGroup。哪种选项更合适取决于你要做什么。如果你只想等待某个结果,那么让goroutine在通道上发送其结果,然后你只需等待它即可。如果你只想等待一些goroutine,那么使用waitgroup。

英文:

(I don't have enough reps to comment?)

> Yes, the issue is with goroutine scheduling and is platform dependent. Use runtime.Gosched at the end of the function with the goroutine to ensure it gets some cputime. Presently on x86_64 linux, the code below will produce "3" but without Gosched it produces "1":

No. That's not the issue, and that only solves it by co-incidence(via an implementation detail). As has already been hinted at in https://stackoverflow.com/questions/19613931/goroutines-defer-not-called-when-main-scope-ends/19614010#19614010 . Your code is racy and you must therefore explicitly synchronize the goroutines. The simplest way is to to use a channel or a waitgroup http://golang.org/pkg/sync/#WaitGroup . Which option is more appropriate depends on what you're doing. If all you want to do is wait for some result, then have the gorutine send its result on the channel and you simply wait for it. If you just want to wait for a number of goroutines, then use a waitgroup.

答案2

得分: 2

这个过程正在结束,所以内部函数被终止。尝试在main函数的最后加上睡眠:

func main(){
    fmt.Println("1")
    defer fmt.Println("-1")
    go func() { 
        fmt.Println("2")
        defer fmt.Println("-2")
        time.Sleep(9 * time.Second)
    }()
    time.Sleep(1 * time.Second)
    fmt.Println("3")
    time.Sleep(9 * time.Second)
}

http://play.golang.org/p/Qrtz_0xiRs

编辑:正如评论中提到的,这不是一种正确的方法。你应该显式地等待goroutine的结束。

英文:

The process is ending, so the internal function is terminated. Try sleeping at the end of main:

func main(){
	fmt.Println("1")
	defer fmt.Println("-1")
	go func() { 
		fmt.Println("2")
		defer fmt.Println("-2")
		time.Sleep(9 * time.Second)
	}()
	time.Sleep(1 * time.Second)
	fmt.Println("3")
	time.Sleep(9 * time.Second)
}

http://play.golang.org/p/Qrtz_0xiRs

EDIT: as mentioned in the comments, this is not the right approach in general. You should explicitly wait for the goroutine.

答案3

得分: 1

是的,问题出在goroutine的调度上,这与平台有关。在函数的末尾使用runtime.Gosched函数来确保goroutine获得一些CPU时间。目前在x86_64 linux上,下面的代码将输出"3",但是如果没有使用Gosched函数,它将输出"1":

q = 1
go func() { 
    q = 2
    defer func() { q = 3 }()
}()
runtime.Gosched()
fmt.Println(q)

你可以在这里查看代码示例:http://play.golang.org/p/dNFQv9IxXv

英文:

Yes, the issue is with goroutine scheduling and is platform dependent. Use runtime.Gosched at the end of the function with the goroutine to ensure it gets some cputime. Presently on x86_64 linux, the code below will produce "3" but without Gosched it produces "1":

q = 1
go func() { 
    q = 2
    defer func() { q = 3 }()
}()
runtime.Gosched()
fmt.Println(q)

http://play.golang.org/p/dNFQv9IxXv

答案4

得分: 1

WaitGroup可以帮助你解决这个问题:

package main

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

func main() {
	fmt.Println("-开始主函数-")
	defer fmt.Println("-延迟执行主函数-")

	wg := sync.WaitGroup{}
	wg.Add(1)

	go func() {
		fmt.Println("-开始协程-")
		defer fmt.Println("-延迟执行协程-")

		time.Sleep(2 * time.Second)
		wg.Done()
	}()

	fmt.Println("-结束主函数-")
	wg.Wait()
}

输出结果:

-开始主函数-
-结束主函数-
-开始协程-
-延迟执行协程-
-延迟执行主函数-
英文:

WaitGroup can help you resolving the issue:

package main

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

func main(){
	fmt.Println("-start main-")
	defer fmt.Println("-defer main-")
	
	wg := sync.WaitGroup{}
	wg.Add(1)
	
	go func() { 
		fmt.Println("-start goroutine-")
		defer fmt.Println("-defer goroutine-")

        time.Sleep(2 * time.Second)
		wg.Done() 
	}()

	fmt.Println("-end main-")
	wg.Wait()
}

Prints:

-start main-
-end main-
-start goroutine-
-defer goroutine-
-defer main-

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

huangapple
  • 本文由 发表于 2013年10月27日 10:11:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/19613931.html
匿名

发表评论

匿名网友

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

确定