在使用sync.WaitGroup时,有更好的方法来退出for循环吗?

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

Better way to exit for loop when using sync.WaitGroup?

问题

在使用sync.WaitGroup时,有没有更好的方法来退出for循环?目前,我使用bool类型的变量e作为触发器,在循环结束时跳出for循环,这个方法是有效的。但是,如果我尝试在检查orders >= quantity的地方跳出for循环,就会出现各种错误。

有没有比我的bool代码更好的方法来跳出这个循环,而不会引发panic错误?

package main

import (
	"fmt"
	"math/rand"
	"strconv"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	var accounts int = 1
	var emails int = 1000

	for i := 0; i < accounts; i++ {
		wg.Add(1)

		go func(i int) {
			defer wg.Done()
			threads := 500 //同时运行的最大子例程数
			var orderMutex sync.Mutex
			var quantity = 2
			var orders = 0
			sem := make(chan bool, threads)
			var wgnested sync.WaitGroup

			var e = false

			for x := 0; x < emails; x++ {
				sem <- true //阻塞

				orderMutex.Lock()
				if orders >= quantity {
					fmt.Println(strconv.Itoa(x) + " Quantity Exceeded Stopping")
					e = true
				}
				orderMutex.Unlock()

				wgnested.Add(1)

				go func() {
					defer wgnested.Done()
					defer func() { <-sem }()

					//模拟订单
					if rand.Intn(50) == 7 {
						orderMutex.Lock()
						orders++
						orderMutex.Unlock()
					}

					fmt.Println(strconv.Itoa(i) + " Made to end ")

				}() //func

				if e {
					break
				}
			} //x

			wgnested.Wait()

		loop:
			for {
				select {
				case <-sem:
					fmt.Println("quit")
					break loop
				default:
					sem <- true
				}
			}
			fmt.Println(strconv.Itoa(i) + " Orders: " + strconv.Itoa(orders))

		}(i)
	} //i

	wg.Wait()
}
英文:

Is there a better way to exit the for loop when using sync.WaitGroup? At the moment I am using bool e as a trigger to break out of the for loop at the end of the loop, which works. If I try to break the for loop where I check orders >= quantity I get all kinds of errors.

Is there a better way to break this loop than my bool code without causing panic?

package main

import (
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;strconv&quot;
	&quot;sync&quot;
)

func main() {
	var wg sync.WaitGroup
	var accounts int = 1
	var emails int = 1000

	for i := 0; i &lt; accounts; i++ {
		wg.Add(1)

		go func(i int) {
			defer wg.Done()
			threads := 500 //Max subroutines to run at once
			var orderMutex sync.Mutex
			var quantity = 2
			var orders = 0
			sem := make(chan bool, threads)
			var wgnested sync.WaitGroup

			var e = false

			for x := 0; x &lt; emails; x++ {
				sem &lt;- true //block

				orderMutex.Lock()
				if orders &gt;= quantity {
					fmt.Println(strconv.Itoa(x) + &quot; Quantity Exceeded Stopping&quot;)
					e = true
				}
				orderMutex.Unlock()

				wgnested.Add(1)

				go func() {
					defer wgnested.Done()
					defer func() { &lt;-sem }()

					//Simulate orders
					if rand.Intn(50) == 7 {
						orderMutex.Lock()
						orders++
						orderMutex.Unlock()
					}

					fmt.Println(strconv.Itoa(i) + &quot; Made to end &quot;)

				}() //func

				if e {
					break
				}
			} //x

			wgnested.Wait()

		loop:
			for {
				select {
				case &lt;-sem:
					fmt.Println(&quot;quit&quot;)
					break loop
				default:
					sem &lt;- true
				}
			}
			fmt.Println(strconv.Itoa(i) + &quot; Orders: &quot; + strconv.Itoa(orders))

		}(i)
	} //i

	wg.Wait()
}

答案1

得分: 2

你可以使用context.Context来在上下文被取消时停止循环。

示例

package main

import (
	"context"
	"fmt"
	"math/rand"
	"strconv"
	"sync"
)

func main() {
	var (
		wg       sync.WaitGroup
		accounts int = 1
		emails   int = 1000
		//
		ctx, cancel = context.WithCancel(context.Background()) // 创建新的上下文
	)

	for i := 0; i < accounts; i++ {
		wg.Add(1)

		go func(i int) {
			// 当goroutine完成时
			// 取消上下文
			defer func() {
				wg.Done()
				cancel()
			}()
			//
			var (
				// threads    = 500 //同时运行的最大子例程数
				orderMutex sync.Mutex
				quantity   = 2
				orders     = 0
				wgnested   sync.WaitGroup
			)

			for x := 0; x < emails; x++ {
				select {
				// 上下文完成的情况
				case <-ctx.Done():
					return
				default:
					// 检查上下文是否被取消
					// 退出循环
					if ctx.Err() != nil {
						return
					}

					orderMutex.Lock()
					if orders >= quantity {
						fmt.Println(strconv.Itoa(x) + " 数量超过限制,停止")
						cancel() // 发送取消上下文的信号
					}
					orderMutex.Unlock()

					wgnested.Add(1)
					go func() {
						defer wgnested.Done()

						// 模拟订单
						if rand.Intn(50) == 7 {
							orderMutex.Lock()
							orders++
							orderMutex.Unlock()
						}

						fmt.Println(strconv.Itoa(x) + " 到达终点") // 我使用了"x"而不是"i"

					}()

					wgnested.Wait()
				}
			}

			//
			fmt.Println(strconv.Itoa(i) + " 订单数: " + strconv.Itoa(orders))
		}(i)
	} //i

	wg.Wait()
}

输出

0 到达终点
1 到达终点
2 到达终点
3 到达终点
4 到达终点
5 到达终点
6 到达终点
...
...
...
88 到达终点
89 到达终点
90 到达终点
91 到达终点
92 到达终点
93 到达终点
94 数量超过限制,停止
94 到达终点
英文:

you use context.Context to stop a loop when the context is cancelled.

Example

package main

import (
	&quot;context&quot;
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;strconv&quot;
	&quot;sync&quot;
)

func main() {
	var (
		wg       sync.WaitGroup
		accounts int = 1
		emails   int = 1000
		//
		ctx, cancel = context.WithCancel(context.Background()) // create new context
	)

	for i := 0; i &lt; accounts; i++ {
		wg.Add(1)

		go func(i int) {
			// when goroutine finish
			// cancel the context
			defer func() {
				wg.Done()
				cancel()
			}()
			//
			var (
				// threads    = 500 //Max subroutines to run at once
				orderMutex sync.Mutex
				quantity   = 2
				orders     = 0
				wgnested   sync.WaitGroup
			)

			for x := 0; x &lt; emails; x++ {
				select {
				// case context is done.
				case &lt;-ctx.Done():
					return
				default:
					// check context is cancelled
					// exit for loop
					if ctx.Err() != nil {
						return
					}

					orderMutex.Lock()
					if orders &gt;= quantity {
						fmt.Println(strconv.Itoa(x) + &quot; Quantity Exceeded Stopping&quot;)
						cancel() // signal cancel context
					}
					orderMutex.Unlock()

					wgnested.Add(1)
					go func() {
						defer wgnested.Done()

						//Simulate orders
						if rand.Intn(50) == 7 {
							orderMutex.Lock()
							orders++
							orderMutex.Unlock()
						}

						fmt.Println(strconv.Itoa(x) + &quot; Made to end &quot;) // i used &quot;x&quot; instead of &quot;i&quot;

					}()

					wgnested.Wait()
				}
			}

			//
			fmt.Println(strconv.Itoa(i) + &quot; Orders: &quot; + strconv.Itoa(orders))
		}(i)
	} //i

	wg.Wait()
}

Output

0 Made to end 
1 Made to end 
2 Made to end 
3 Made to end 
4 Made to end 
5 Made to end 
6 Made to end 
...
...
...
88 Made to end 
89 Made to end 
90 Made to end 
91 Made to end 
92 Made to end 
93 Made to end 
94 Quantity Exceeded Stopping
94 Made to end 

huangapple
  • 本文由 发表于 2021年9月12日 11:57:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/69148323.html
匿名

发表评论

匿名网友

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

确定