请Go在继续之前运行所有的goroutine。

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

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 (
	&quot;log&quot;
	&quot;runtime&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

var wg sync.WaitGroup

func main() {
	wg.Add(1)
	//run multiples routines on a huge system
	go start()
	wg.Wait()
}
func start() {
	log.Println(&quot;Start&quot;)
	chanStopStream := make(chan bool)
	go stopStream(chanStopStream)
	select {
	case &lt;-chanStopStream:
		log.Println(&quot;receive chan&quot;)
	case &lt;-time.After(time.Second): //if stopStream hangs do not wait more than 1 second
		log.Println(&quot;TIMER&quot;)
		//call some crash alert
	}
	time.Sleep(3 * time.Second)
	log.Println(&quot;end&quot;)
	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 &lt;- true:
	default: //select/default is used because if the caller times out this routine will hangs forever
		log.Println(&quot;default&quot;)
	}
}

答案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(&quot;Start&quot;)
	chanStopStream := make(chan bool, 1) // &lt;--- buffered channel
	go stopStream(chanStopStream)
    ...
}

func stopStream(retChan chan bool) {
    ...
    // Always send. No select/default needed.
	retChan &lt;- 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(&quot;Start&quot;)
	chanStopStream := make(chan bool) // buffered channel not required
	go stopStream(chanStopStream)
    ...

func stopStream(retChan chan bool) {
    ...
    close(retChan)
}

https://go.dev/play/p/lNfT6qzrMmO

huangapple
  • 本文由 发表于 2023年5月20日 06:57:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76292860.html
匿名

发表评论

匿名网友

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

确定