如何停止多个 goroutine?

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

how to stop multiple go routines

问题

你好!根据你的描述,你想在一个循环中调用goroutine/线程,并且希望在某个goroutine/线程成功执行后停止其他所有的goroutine/线程。你想知道是否有实现这个功能的方法。

是的,你可以使用Go语言中的通道(channel)来实现这个功能。你可以创建一个通道来控制goroutine/线程的执行和停止。在循环中,你可以启动多个goroutine/线程,并将通道作为参数传递给它们。当某个goroutine/线程成功执行时,它可以向通道发送一个信号,其他goroutine/线程可以通过接收通道的信号来停止执行。

以下是一个简单的示例代码,演示了如何使用通道来实现你的需求:

package main

import (
	"fmt"
	"sync"
)

func worker(id int, wg *sync.WaitGroup, stopCh <-chan struct{}) {
	defer wg.Done()

	for {
		select {
		case <-stopCh:
			fmt.Printf("Worker %d stopped\n", id)
			return
		default:
			// 执行具体的工作任务
			fmt.Printf("Worker %d is working\n", id)
		}
	}
}

func main() {
	stopCh := make(chan struct{})
	wg := sync.WaitGroup{}

	// 启动多个goroutine/线程
	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go worker(i, &wg, stopCh)
	}

	// 模拟某个goroutine/线程成功执行后停止其他所有goroutine/线程
	go func() {
		// 假设第3个goroutine/线程成功执行
		// 在这里你可以根据具体的条件来判断哪个goroutine/线程成功执行
		// 当某个goroutine/线程成功执行时,向通道发送一个信号
		stopCh <- struct{}{}
	}()

	// 等待所有goroutine/线程执行完毕
	wg.Wait()
}

在上面的示例代码中,我们创建了一个通道stopCh来控制goroutine/线程的停止。在worker函数中,我们使用select语句来监听通道的信号。如果收到了通道的信号,表示需要停止执行,那么该goroutine/线程就会退出。

main函数中,我们启动了多个goroutine/线程,并将通道stopCh作为参数传递给它们。然后,我们通过另一个goroutine来模拟某个goroutine/线程成功执行后停止其他所有goroutine/线程,它向通道stopCh发送了一个信号。

最后,我们使用sync.WaitGroup来等待所有的goroutine/线程执行完毕。

希望这个示例能帮助到你!如果有任何问题,请随时提问。

英文:

i have to invoke goroutuine /thread from a loop. Because of the loop, there are many go routines executes in parallel. If any routine/thread executed successfully, then i have to stop all other thread/routine.

Is there any way to implement this?

答案1

得分: 2

你可以像Burak所回答的那样,除了上下文之外,还可以使用退出通道。


package main

import (
	"fmt"
	"math/rand"
	"time"
)

func foo(channel, quit chan string, i int) {

	channel <- fmt.Sprintf("goroutine %d started!", i)
	for {
		rand.Seed(time.Now().UnixNano())
		time.Sleep(time.Duration(rand.Intn(500)+500) * time.Millisecond)
		quit <- fmt.Sprintf("goRoutine %d completed!", i)
	}
}
func main() {

	channel := make(chan string)
	quit := make(chan string)

	for i := 0; i < 3; i++ {
		go foo(channel, quit, i)
	}
	
	for {
		select {
		case update:= <-channel:
			fmt.Println(update)
		case quit:= <-quit:
			fmt.Println(quit)
			return
		}
	}
}

英文:

You can use quit channels in addition to context as answered by Burak.


package main

import (
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;time&quot;
)

func foo(channel, quit chan string, i int) {

	channel &lt;- fmt.Sprintf(&quot;goroutine %d started!&quot;, i)
	for {
		rand.Seed(time.Now().UnixNano())
		time.Sleep(time.Duration(rand.Intn(500)+500) * time.Millisecond)
		quit &lt;- fmt.Sprintf(&quot;goRoutine %d completed!&quot;, i)
	}
}
func main() {

	channel := make(chan string)
	quit := make(chan string)

	for i := 0; i &lt; 3; i++ {
		go foo(channel, quit, i)
	}
	
	for {
		select {
		case update:= &lt;-channel:
			fmt.Println(update)
		case quit:= &lt;-quit:
			fmt.Println(quit)
			return
		}
	}
}

答案2

得分: 1

你可以使用上下文(context):

ctx, cancel := context.WithCancel(context.Background())
for ... {
   go func() {
      defer cancel() // 一旦这个 goroutine 结束,取消上下文
      doStuff(ctx)
   }()
}

你必须在 goroutine 中检查上下文是否被取消:

func doStuff(ctx context.Context) {
   ...
   if ctx.Err() != nil {
       // 已取消,返回
       return
   }
   ...
}

这并不能保证一旦上下文被取消,其他 goroutine 会立即结束,但它保证了如果你正确检查上下文是否被取消,所有的 goroutine 最终都会结束。

英文:

You can use a context:

ctx, cancel:= context.WithCancel(context.Background())
for ... {
   go func() {
      defer cancel() // cancel context once this goroutine ends
      doStuff(ctx)
   }()
}

You have to check context cancellation in the goroutines:

func doStuff(ctx context.Context) {
   ...
   if ctx.Err()!=nil {
       // Canceled, return
       return
   }
  ...
}

This will not guarantee that once the context is canceled other goroutines will end immediately, however it guarantees that, if you check for context cancellation correctly, all goroutines will end eventually.

答案3

得分: 0

你可以使用上下文(context)和 errgroup(错误组)来实现。大致的代码如下:

package main

import (
	"context"
	"fmt"
	"math/rand"
	"os"
	"time"

	"golang.org/x/sync/errgroup"
)

func doStuff(ctx context.Context, i int) error {
	count := 0
	for ctx.Err() == nil {
		// 做一些操作
		time.Sleep(time.Millisecond * time.Duration(rand.Intn(500)))
		count++
		if count > 6 { // 错误条件
			fmt.Fprintf(os.Stdout, "%v 达到计数 %v\n", i, count)
			return fmt.Errorf("错误 %v, 计数 %v\n", i, count)
		}
	}
	fmt.Fprintf(os.Stdout, "被终止 %v @ 计数 %v\n", i, count)
	return ctx.Err()
}

func main() {
	rand.Seed(int64(time.Now().Nanosecond()))
	ctxWc, _ := context.WithCancel(context.Background())
	g, ctx := errgroup.WithContext(ctxWc)
	for i := 0; i < 5; i++ {
		i := i
		g.Go(func() error {
			return doStuff(ctx, i)
		})
	}
	err := g.Wait()
	fmt.Println("结束")
	if err != nil {
		fmt.Println(err)
	}
}

希望对你有帮助!

英文:

You could use a context and an errgroup.
Something along the lines of...

package main

import (
	&quot;context&quot;
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;os&quot;
	&quot;time&quot;

	&quot;golang.org/x/sync/errgroup&quot;
)

func doStuff(ctx context.Context, i int) error {
	count := 0
	for ctx.Err() == nil {
		// do the stuff
		time.Sleep(time.Millisecond * time.Duration(rand.Intn(500)))
		count++
		if count &gt; 6 { // error condition
			fmt.Fprintf(os.Stdout, &quot;%v reached count %v\n&quot;, i, count)
			return fmt.Errorf(&quot;Error %v, count %v\n&quot;, i, count)
		}
	}
	fmt.Fprintf(os.Stdout, &quot;Killed %v @ count %v\n&quot;, i, count)
	return ctx.Err()
}

func main() {
	rand.Seed(int64(time.Now().Nanosecond()))
	ctxWc, _ := context.WithCancel(context.Background())
	g, ctx := errgroup.WithContext(ctxWc)
	for i := 0; i &lt; 5; i++ {
		i := i
		g.Go(func() error {
			return doStuff(ctx, i)
		})
	}
	err := g.Wait()
	fmt.Println(&quot;The End&quot;)
	if err != nil {
		fmt.Println(err)
	}
}

huangapple
  • 本文由 发表于 2021年8月2日 01:11:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/68612683.html
匿名

发表评论

匿名网友

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

确定