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
的错误原因来确定是否因为错误而提前结束。