10个goroutine想要顺序执行, 如何实现?

在Go语言中,要让10个goroutine按照特定顺序执行,可以使用channel和同步原语(如sync.WaitGroup)来实现。这里提供一种常见的方式,通过channel传递令牌(token)来确保goroutine按照预定顺序执行:

package main

import (
  "fmt"
  "sync"
)

func main() {
  tokenChan := make(chan int, 1) // 创建一个带缓冲的channel,容量为1
  var wg sync.WaitGroup

  for i := 1; i <= 10; i++ {
    wg.Add(1) // 对每个goroutine增加计数器

    go func(id int) {
      defer wg.Done()

      // 从channel接收令牌,阻塞直到收到
      <-tokenChan

      fmt.Printf("Executing goroutine %d\n", id)

      // 执行完当前goroutine的任务后,向channel发送一个新的令牌,供下一个goroutine使用
      tokenChan <- id
    }(i)

    // 第一次循环时,需要向channel发送一个初始令牌,启动执行
    if i == 1 {
      tokenChan <- 0
    }
  }

  // 等待所有goroutine完成
  wg.Wait()
}

这段代码中,创建了一个带缓冲为1的channel tokenChan,并为每个goroutine启动时加入一个计数到sync.WaitGroup中。每个goroutine在执行时首先从channel中接收令牌,只有拿到令牌的goroutine才能继续执行。执行完毕后,goroutine会向channel中重新放入一个令牌,这样下一个goroutine才能获得执行权限。

这样一来,goroutine们将按照顺序获取和释放令牌,从而实现按序执行。最后,通过WaitGroup确保所有goroutine都执行完毕。

10个协程同时执行, 如果其中有一个协程产生了错误, 其它协程停止运行, 如何实现?

在Go语言中,要实现当10个协程(goroutine)中有一个产生错误时,其它协程都能停止运行,可以借助context包来统一控制所有协程的生命周期。context包提供了方便的方式来传播取消信号,并且可以跟踪协程的完成情况。下面是一个使用context.Context的示例:

package main

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

func worker(ctx context.Context, id int, wg *sync.WaitGroup) error {
  defer wg.Done()

  for {
    select {
    case <-ctx.Done():
      // 当收到Done信号时,说明应该结束当前协程
      fmt.Printf("Worker %d received cancellation signal and is stopping.\n", id)
      return ctx.Err() // 返回上下文取消的错误
    default:
      // 此处代表正常的业务逻辑,如果发生错误,则返回该错误
      err := doWork()
      if err != nil {
        fmt.Printf("Worker %d encountered an error: %v\n", id, err)
        return err
      }
    }
  }
}

func main() {
  var wg sync.WaitGroup
  ctx, cancel := context.WithCancel(context.Background())

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

    go func(id int) {
      defer wg.Done()
      err := worker(ctx, id, &wg)
      if err != nil {
        cancel() // 当任意一个worker返回错误时,取消整个context
      }
    }(i)
  }

  // 等待所有协程执行完毕,或者在其中一个协程出错时通过cancel函数提前结束所有协程
  wg.Wait()

  // 检查是否是因为取消而结束
  if err := ctx.Err(); err != nil && err != context.Canceled {
    fmt.Println("An unexpected error occurred:", err)
  } else {
    fmt.Println("All workers completed successfully or were canceled.")
  }
}

在这个示例中,我们创建了一个可以取消的context,并在每个协程中监听这个context的取消信号。当任一协程在执行过程中发现错误时,它会调用cancel函数来取消context,这样其他所有还在监听context.Done()的协程都会收到信号并停止工作。同时,main函数通过WaitGroup等待所有协程结束,并检查context的错误原因来确定是否因为错误而提前结束。

最后编辑: kuteng  文档更新时间: 2024-04-02 09:53   作者:kuteng