设计思路
- 使用缓冲通道实现:
- 缓冲通道用于控制A类任务的执行数量。当A类任务执行到一定数量(例如5个)时,向通道发送信号,B类任务开始执行。
- 另一个非缓冲通道用于确保B类任务执行完成后,A类任务继续执行。
- 使用非缓冲通道实现:
- 非缓冲通道用于同步A类任务和B类任务的执行。A类任务执行一定数量后,通过非缓冲通道与B类任务进行同步,B类任务执行完后,再通过非缓冲通道通知A类任务继续执行。
代码实现
- 使用缓冲通道:
package main
import (
"fmt"
)
func main() {
aTaskCount := 5
aTaskDone := make(chan struct{}, aTaskCount)
bTaskDone := make(chan struct{})
// A类任务
go func() {
for i := 0; i < 10; i++ {
fmt.Println("A类任务执行:", i)
if i < aTaskCount {
aTaskDone <- struct{}{}
}
if i >= aTaskCount {
<-bTaskDone
}
}
}()
// B类任务
go func() {
for i := 0; i < aTaskCount; i++ {
<-aTaskDone
}
fmt.Println("B类任务开始执行")
// 模拟B类任务执行
fmt.Println("B类任务执行完毕")
bTaskDone <- struct{}{}
}()
select {}
}
- 使用非缓冲通道:
package main
import (
"fmt"
)
func main() {
aTaskCount := 5
aTaskDone := make(chan struct{})
bTaskDone := make(chan struct{})
// A类任务
go func() {
for i := 0; i < 10; i++ {
fmt.Println("A类任务执行:", i)
if i == aTaskCount {
close(aTaskDone)
}
if i >= aTaskCount {
<-bTaskDone
}
}
}()
// B类任务
go func() {
for i := 0; i < aTaskCount; i++ {
<-aTaskDone
}
fmt.Println("B类任务开始执行")
// 模拟B类任务执行
fmt.Println("B类任务执行完毕")
bTaskDone <- struct{}{}
}()
select {}
}
选择不同通道类型的原因
- 缓冲通道:
- 优点:缓冲通道可以在不阻塞发送操作的情况下,存储一定数量的值。在这个场景中,缓冲通道
aTaskDone
可以先存储A类任务执行的信号,直到达到设定的数量,这样A类任务可以继续执行而不会阻塞。
- 适用场景:适用于需要一定数量的任务先执行,然后触发其他任务执行,并且在触发之前不需要立即同步的场景。
- 非缓冲通道:
- 优点:非缓冲通道在发送和接收操作上是同步的,发送操作会阻塞直到有接收者准备好接收数据,反之亦然。在这个场景中,非缓冲通道
aTaskDone
可以确保A类任务执行到一定数量后,才与B类任务进行同步,保证了任务执行的顺序。
- 适用场景:适用于需要严格同步任务执行顺序,确保一个任务执行到某个阶段后,另一个任务才能开始执行的场景。