使用Go语言时,可以使用goroutine和channel来接收错误或成功的信息。

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

go routines and a channel to receive error or success

问题

我有一个函数,我想定义最大的goroutine数量。我有一个列表,我遍历这个列表,并通过通道向goroutine发送消息。在这个goroutine中,我将调用一个函数,该函数将返回一个答案或一个错误。当不是错误时,我想将返回值保存在一个切片中,当出现错误时,我想停止goroutine并进行调用。
但是我无法做到当出现错误时,所有的goroutine都结束,并且我需要错误的值。

type response struct {
	value string
}

func Testing() []response {

	fakeValues := getFakeValues()

	maxParallel := 25
	wg := &sync.WaitGroup{}
	wg.Add(maxParallel)

	if len(fakeValues) < maxParallel {
		maxParallel = len(fakeValues)
	}

	errReceive := make(chan error, 1)
	defer close(errReceive)

	response := make([]response, 0)
	valuesChan := make(chan string, 1)

	for i := 0; i < maxParallel; i++ {
		go func(valuesChan <-chan string, errReceive chan error) {
			for value := range valuesChan {
				resp, err := getFakeResult(value)
				if err != nil {
					errReceive <- err
				}

				response = append(response, resp)
			}
			wg.Done()
		}(valuesChan, errReceive)
	}

	for _, val := range fakeValues {
		valuesChan <- val
	}

	close(valuesChan)
	wg.Wait()

	err := <-errReceive
	if err != nil {
		// make any thing
	}

	return response
}

func getFakeValues() []string {
	return []string{"a", "b"}
}

func getFakeResult(val string) (response, error) {
	if val == "a" {
		return response{}, fmt.Errorf("ooh noh:%s", val)
	}

	return response{
		value: val,
	}, nil
}

以上是您提供的代码的翻译结果。

英文:

I have a function that I want to define a maximum number of go routines, I have a list and I go through this list and I send a message to the go routines through the channel, and in this go routine I will call a function that will either get an answer or an err, when it's not an err I want to save the return in a slice, and when it's an err I want to stop the go routines and make a call.
but I'm not able to make it so that when I have an error, all the go routines end, and I need the value of err

type response struct {
value string
}
func Testing() []response {
fakeValues := getFakeValues()
maxParallel := 25
wg := &amp;sync.WaitGroup{}
wg.Add(maxParallel)
if len(fakeValues) &lt; maxParallel {
maxParallel = len(fakeValues)
}
errReceive := make(chan error, 1)
defer close(errReceive)
response := make([]response, 0)
valuesChan := make(chan string, 1)
for i := 0; i &lt; maxParallel; i++ {
go func(valuesChan &lt;-chan string, errReceive chan error) {
for value := range valuesChan {
resp, err := getFakeResult(value)
if err != nil {
errReceive &lt;- err
}
response = append(response, resp)
}
wg.Done()
}(valuesChan, errReceive)
}
for _, val := range fakeValues {
valuesChan &lt;- val
}
close(valuesChan)
wg.Wait()
err := &lt;-errReceive
if err != nil {
// make any thing
}
return response
}
func getFakeValues() []string {
return []string{&quot;a&quot;, &quot;b&quot;}
}
func getFakeResult(val string) (response, error) {
if val == &quot;a&quot; {
return response{}, fmt.Errorf(&quot;ooh noh:%s&quot;, val)
}
return response{
value: val,
}, nil
}

答案1

得分: 1

你可以使用带有cancel的上下文(context)来通知goroutine停止执行。

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

wg := &sync.WaitGroup{}
wg.Add(1)
go func(ctx context.Context) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			fmt.Println("context is done")
			return
		case <-time.After(time.Second):
			fmt.Println("work")
		}
	}
}(ctx)

time.Sleep(time.Second * 5)
cancel()
wg.Wait()

这是一个更好地展示了上下文在你的用例中的示例。

type result struct {
	err error
	val int
}
rand.Seed(time.Now().UnixNano())

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

rchan := make(chan result, 5)
wg := &sync.WaitGroup{}

for i := 0; i < 5; i++ {
	wg.Add(1)
	go func(ctx context.Context) {
		defer wg.Done()
		for {
			select {
			case <-ctx.Done():
				fmt.Println("context is done")
				return
			case <-time.After(time.Second):
				n := rand.Intn(100)
				if n > 90 {
					rchan <- result{err: fmt.Errorf("error %d", n)}
				} else {
					rchan <- result{val: n}
				}
			}
		}
	}(ctx)
}

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

for res := range rchan {
	if res.err != nil {
		fmt.Println(res.err)
		cancel()
		break
	} else {
		fmt.Println(res.val)
	}
}

你可以在这里查看代码:https://go.dev/play/p/Z63n1h2A81o

英文:

You can use a context with cancel and use it to let the go routines know they should stop.

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wg := &amp;sync.WaitGroup{}
wg.Add(1)
go func(ctx context.Context) {
defer wg.Done()
for {
select {
case &lt;-ctx.Done():
fmt.Println(&quot;context is done&quot;)
return
case &lt;-time.After(time.Second):
fmt.Println(&quot;work&quot;)
}
}
}(ctx)
time.Sleep(time.Second * 5)
cancel()
wg.Wait()

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


Here is an example that showcases it better in the context of your use case.

type result struct {
err error
val int
}
rand.Seed(time.Now().UnixNano())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
rchan := make(chan result, 5)
wg := &amp;sync.WaitGroup{}
for i := 0; i &lt; 5; i++ {
wg.Add(1)
go func(ctx context.Context) {
defer wg.Done()
for {
select {
case &lt;-ctx.Done():
fmt.Println(&quot;context is done&quot;)
return
case &lt;-time.After(time.Second):
n := rand.Intn(100)
if n &gt; 90 {
rchan &lt;- result{err: fmt.Errorf(&quot;error %d&quot;, n)}
} else {
rchan &lt;- result{val: n}
}
}
}
}(ctx)
}
go func() {
wg.Wait()
close(rchan)
}()
for res := range rchan {
if res.err != nil {
fmt.Println(res.err)
cancel()
break
} else {
fmt.Println(res.val)
}
}

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

huangapple
  • 本文由 发表于 2022年4月13日 01:09:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/71846710.html
匿名

发表评论

匿名网友

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

确定