Golang:并发任务处理器
要求:给定n个task
任务的数组tasks
,和执行函数excute()
,构造一个函数handle()
,并发执行task
任务,并且同一时间允许的最大并发调用数为10
。
内容提要:管道channel
管道的定义:
// 10个缓冲区的chan; make(chan chan类型,缓冲区大小)
var cacheCh = make(chan struct{},10)
// 无缓冲区的chan
var ch = make(chan struct{})
管道的传值:
管道变量之后使用符号<-
+值
。即可向管道中传入值,例如:
// 向管道中插入一个空的结构体
ch <- struct{}{}
管道的值获取:
在管道变量之前使用<-
表示冲管道中取值,如果管道中没有值,会阻塞。
// 将从管道中获取到的值赋值到变量value中.如果没有值会阻塞直到获取到值
value := <- ch
有缓冲区和无缓冲区的区别:
当管道中的值还没有被消耗完,且管道已满。再向管道中传值会阻塞,直到缓冲区有空位。
// 定义一个两个缓冲区的管道,然后插入3次值
ch2 := make(chan int, 2)
// 向管道中传值
go func() {
for i := 1; i <= 3; i++ {
ch2 <- i
fmt.Println("插入成功")
}
}()
time.Sleep(time.Second * 3)
fmt.Println("等待3秒。。。取出一个")
<-ch2
// 防止程序提前退出,sleep 1 秒
time.Sleep(time.Second * 1)
// 第三次插入因为没有缓冲区了,会阻塞3秒
/* 输出结果:
插入成功
插入成功
等待3秒。。。取出一个
插入成功
*/
无缓冲区的死锁:
// 定义一个无缓冲区的chan
ch := make(chan struct{})
// 向管道传值
ch <- struct{}{}
fmt.Println("插入成功")
// 取值
<-ch
fmt.Println("取出成功")
/* 输出:
fatal error: all goroutines are asleep - deadlock!
*/
// 无缓冲区就要求传值和接收值必须都准备好
无缓冲区,但是由协程异步传值,不会死锁
// 定义一个无缓冲区的chan
ch := make(chan int)
// 向管道传值
go func() {
// 必须在协程中,如果是同步的话会死锁
ch <- struct{}{}
fmt.Println("插入成功")
}()
// 取值
<-ch
fmt.Println("取出成功")
/*输出结果:
插入成功
取出成功
*/
内容提要:sync.WatiGroup{}
用于等待多个协程处理完返回结果。例如有个请求过来,需要去读取数据库、还有操作文件、请求第三方接口,为了加快处理数据,你选择了将这些异步出来,独立执行,最后都处理完后将结果整合返回。
这里会有个问题,你无法感知异步任务执行情况怎么样了,如果有的任务还没处理完主程序就返回了,结果会不完整。不符合预期。
sync.WatiGroup{}
可以解决这个等待问题。
-
WatiGroup.Add(num int)
可以添加等待的任务数,num表示任务个数
-
WatiGroup.Done()
执行后表示一个任务处理完毕 -
WatiGroup.Wait()
会阻塞,直到所有的任务都Done
处理完毕。
异步处理三个耗时任务,等待所有任务处理完毕返回结果:
wg := sync.WaitGroup{}
for i := 1; i <= 3; i++ {
wg.Add(1)
go func() {
fmt.Println("处理耗时2秒的任务")
time.Sleep(time.Second * 2)
fmt.Println("处理一个任务")
wg.Done()
}()
}
fmt.Println("等待所有结果返回")
wg.Wait()
fmt.Println("处理完成啦")
执行结果:
go run .\main.go
处理耗时2秒的任务
处理耗时2秒的任务
处理耗时2秒的任务
等待所有结果返回
处理一个任务
处理一个任务
处理一个任务
处理完成啦
题干实现思路:
缓冲区的管道用户限制最高并发数,而sync.WaitGroup
用于等待所有任务处理完毕。
import (
"sync"
)
type Task int
func Handle(tasks []Task, excute func(task Task)) {
wg := sync.WaitGroup{}
ch := make(chan struct{}, 10)
for i := range tasks {
// 每执行一个任务,向管道中传入一个值,表示有个任务。如果满了则阻塞
ch <- struct{}{}
wg.Add(1)
task := tasks[i]
go func() {
defer wg.Done()
excute(task)
// 任务处理完,向管道中取出一个值,表示处理完一个
<-ch
}()
}
wg.Wait()
}