尝试停止创建更多的goroutine时发生了恐慌。

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

Panic while trying to stop creating more goroutines

问题

我正在尝试并行调用一个API以加快速度,但我面临一个问题:如果我从其中一个goroutine调用中收到错误,我需要停止启动goroutine来调用API。由于我在错误处理部分和执行完成时都关闭了通道,所以我得到了一个panic: close of closed channel错误。有没有一种优雅的方式来处理这个问题,而不会导致程序崩溃?任何帮助将不胜感激!

以下是伪代码片段。

for i := 0; i < someNumber; i++ {
	go func(num int, q chan<- bool) {
		value, err := callAnAPI()
		if err != nil {
			close(q)//退出for循环
		}
		// 在这里处理value
		wg.Done()
	}(i, quit)
}
close(quit)

为了模拟我的场景,我编写了以下程序。有没有办法在满足条件(已注释)后优雅地退出for循环?

package main

import (
	"fmt"
	"sync"
)

func receive(q <-chan bool) {
	for {
		select {
		case <-q:
			return
		}
	}
}

func main() {
	quit := make(chan bool)

	var result []int
	wg := &sync.WaitGroup{}
	wg.Add(10)
	for i := 0; i < 10; i++ {
		go func(num int, q chan<- bool) {
			//if num == 5 {
			//	close(q)
			//}
			result = append(result, num)

			wg.Done()
		}(i, quit)
	}
	close(quit)
	receive(quit)

	wg.Wait()

	fmt.Printf("Result: %v", result)
}
英文:

I'm trying to parallelize calls to an API to speed things up, but I'm facing a problem where I need to stop spinning up goroutines to call the API if I receive an error from one of the goroutine calls. Since I am closing the channel twice(once in the error handling part and when the execution is done), I'm getting a panic: close of closed channel error. Is there an elegant way to handle this without the program to panic? Any help would be appreciated!

The following is the pseudo-code snippet.

for i := 0; i &lt; someNumber; i++ {
	go func(num int, q chan&lt;- bool) {
		value, err := callAnAPI()
		if err != nil {
			close(q)//exit from the for-loop
		}
		// process the value here
		wg.Done()
	}(i, quit)
}
close(quit)

To mock my scenario, I have written the following program. Is there any way to exit the for-loop gracefully once the condition(commented out) is satisfied?

package main

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

func receive(q &lt;-chan bool) {
	for {
		select {
		case &lt;-q:
			return
		}
	}
}

func main() {
	quit := make(chan bool)

	var result []int
	wg := &amp;sync.WaitGroup{}
	wg.Add(10)
	for i := 0; i &lt; 10; i++ {
		go func(num int, q chan&lt;- bool) {
			//if num == 5 {
			//	close(q)
			//}
			result = append(result, num)

			wg.Done()
		}(i, quit)
	}
	close(quit)
	receive(quit)

	wg.Wait()

	fmt.Printf(&quot;Result: %v&quot;, result)
}

答案1

得分: 0

你可以使用context包,该包定义了Context类型,用于在API边界和进程之间传递截止时间、取消信号和其他请求范围的值。

package main

import (
	"context"
	"fmt"
	"sync"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // 在完成后取消,即使没有错误

	wg := &sync.WaitGroup{}

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(num int) {
			defer wg.Done()
			select {
			case <-ctx.Done():
				return // 发生错误,终止
			default: // 避免阻塞
			}

			// 在这里编写你的代码
			// res, err := callAnAPI()
			// if err != nil {
			// 	cancel()
			//	return
			//}

			if num == 5 {
				cancel()
				return
			}

			fmt.Println(num)

		}(i)
	}

	wg.Wait()

	fmt.Println(ctx.Err())
}

Go Playground上尝试。

你还可以查看这个答案以获取更详细的解释。

英文:

You can use context package which defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.

package main

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

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // cancel when we are finished, even without error

	wg := &amp;sync.WaitGroup{}

	for i := 0; i &lt; 10; i++ {
		wg.Add(1)
		go func(num int) {
			defer wg.Done()
			select {
			case &lt;-ctx.Done():
				return // Error occured somewhere, terminate
			default: // avoid blocking
			}
			
			// your code here
			// res, err := callAnAPI()
			// if err != nil {
			// 	cancel()
			//	return
			//}
			
			if num == 5 {
				cancel()
				return
			}

			fmt.Println(num)

		}(i)
	}
	
	wg.Wait()
	
	fmt.Println(ctx.Err())
}

Try on: Go Playground

You can also take a look to this answer for more detailed explanation.

huangapple
  • 本文由 发表于 2020年2月5日 11:29:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/60068633.html
匿名

发表评论

匿名网友

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

确定