英文:
Ask Go to run all goroutines before continuing
问题
我需要让Golang调度程序在继续之前运行所有的goroutine,runtime.Gosched()无法解决这个问题。
问题在于goroutine可能运行得非常快,以至于start()函数中的"select"在stopStream()函数中的"select"之后运行,然后"case <-chanStopStream:"接收器还没有准备好接收"case retChan <- true:"的发送器。
结果是当发生这种情况时,结果与stopStream()挂起时的行为相同。
运行以下代码https://go.dev/play/p/DQ85XqjU2Q_z
多次,你会看到以下两个响应:
当不挂起时的预期响应:
2009/11/10 23:00:00 Start
2009/11/10 23:00:00 receive chan
2009/11/10 23:00:03 end
当挂起时的预期响应,但当速度很快时不会出现:
2009/11/10 23:00:00 Start
2009/11/10 23:00:00 default
2009/11/10 23:00:01 TIMER
2009/11/10 23:00:04 end
代码如下:
package main
import (
"log"
"runtime"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
wg.Add(1)
//在一个庞大的系统上运行多个goroutine
go start()
wg.Wait()
}
func start() {
log.Println("Start")
chanStopStream := make(chan bool)
go stopStream(chanStopStream)
select {
case <-chanStopStream:
log.Println("receive chan")
case <-time.After(time.Second): //如果stopStream挂起,不要等待超过1秒
log.Println("TIMER")
//调用一些崩溃警报
}
time.Sleep(3 * time.Second)
log.Println("end")
wg.Done()
}
func stopStream(retChan chan bool) {
//做一些可能比调用者快或慢的工作
runtime.Gosched()
//time.Sleep(time.Microsecond) //比runtime.Gosched()更好的解决方案
//time.Sleep(2 * time.Second) //模拟当这个goroutine挂起超过1秒时的情况
select {
case retChan <- true:
default: //使用select/default是因为如果调用者超时,这个goroutine将永远挂起
log.Println("default")
}
}
英文:
I need that the Golang scheduler run all goroutines before continue, runtime.Gosched() does not solve.
The problem is that the go routine can run so fast that the "select" in start() run after the "select" inside de stopStream(), then the "case <-chanStopStream:" receiver is not ready to the sender "case retChan <- true:".
The result is that when this happen the result is the same behavior from when stopStream() hangs
Run this code https://go.dev/play/p/DQ85XqjU2Q_z
many times that you will see this both responses
Expected response when NOT hang:
2009/11/10 23:00:00 Start
2009/11/10 23:00:00 receive chan
2009/11/10 23:00:03 end
Expected response when hang, but not when is so fast:
2009/11/10 23:00:00 Start
2009/11/10 23:00:00 default
2009/11/10 23:00:01 TIMER
2009/11/10 23:00:04 end
The code
package main
import (
"log"
"runtime"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
wg.Add(1)
//run multiples routines on a huge system
go start()
wg.Wait()
}
func start() {
log.Println("Start")
chanStopStream := make(chan bool)
go stopStream(chanStopStream)
select {
case <-chanStopStream:
log.Println("receive chan")
case <-time.After(time.Second): //if stopStream hangs do not wait more than 1 second
log.Println("TIMER")
//call some crash alert
}
time.Sleep(3 * time.Second)
log.Println("end")
wg.Done()
}
func stopStream(retChan chan bool) {
//do some work that can be faster then caller or not
runtime.Gosched()
//time.Sleep(time.Microsecond) //better solution then runtime.Gosched()
//time.Sleep(2 * time.Second) //simulate when this routine hangs more than 1 second
select {
case retChan <- true:
default: //select/default is used because if the caller times out this routine will hangs forever
log.Println("default")
}
}
答案1
得分: 1
在执行当前goroutine之前,没有一种方法可以运行所有其他的goroutine。
通过确保goroutine不会在stopStream
上阻塞来解决这个问题:
选项1:将chanStopStream
更改为缓冲通道。这样可以确保stopStream
可以发送一个值而不会阻塞。
func start() {
log.Println("Start")
chanStopStream := make(chan bool, 1) // <-- 缓冲通道
go stopStream(chanStopStream)
...
}
func stopStream(retChan chan bool) {
...
// 总是发送。不需要select/default。
retChan <- true
}
https://go.dev/play/p/xWg42TO_fIW
选项2:关闭通道而不是发送一个值。发送方始终可以关闭通道。从关闭的通道接收将返回通道值类型的零值。
func start() {
log.Println("Start")
chanStopStream := make(chan bool) // 不需要缓冲通道
go stopStream(chanStopStream)
...
}
func stopStream(retChan chan bool) {
...
close(retChan)
}
https://go.dev/play/p/lNfT6qzrMmO
英文:
There is not a way to run all other goroutines before continuing execution of the current goroutine.
Fix the problem by ensuring that the goroutine does not block on stopStream
:
Option 1: Change chanStopStream
to a buffered channel. This ensures that stopStream
can send a value without blocking.
func start() {
log.Println("Start")
chanStopStream := make(chan bool, 1) // <--- buffered channel
go stopStream(chanStopStream)
...
}
func stopStream(retChan chan bool) {
...
// Always send. No select/default needed.
retChan <- true
}
https://go.dev/play/p/xWg42TO_fIW
Option 2: Close the channel instead of sending a value. A channel can always be closed by the sender. Receive on a closed channel returns the zero value for the channel's value type.
func start() {
log.Println("Start")
chanStopStream := make(chan bool) // buffered channel not required
go stopStream(chanStopStream)
...
func stopStream(retChan chan bool) {
...
close(retChan)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论