Golang:任务并发调用

1,089 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情

Golang:并发任务处理器

要求:给定n个task任务的数组tasks,构造一个函数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{}可以解决这个等待问题。

  1. WatiGroup.Add(num int)可以添加等待的任务数,num表示任务个数

  2. WatiGroup.Done()执行后表示一个任务处理完毕

  3. 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()

}