Go 并发时限制最大并发度并获取协程结果的两种方式;for range 和for index获取缓冲通道

1.要并发执行的大量任务,假如300个

package main

import "fmt"

func task() {
	fmt.Println("task...")
}

func main() {
	for i := 0; i < 300; i++ {
		go task()
	}
    println("main over...")
}

执行如上代码,会出现task协程全部未运行结束main协程就运行结束了。

但是往往我们需要task协程全部运行结束才让main协程运行结束,那么我们就要再改善下上述代码

2.让大量并发任务运行结束后才main协程才运行结束

 

package main

import (
	"fmt"
	"sync"
)

func task(wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Println("task...")
}

func main() {
	var wg sync.WaitGroup
	for i := 0; i < 300; i++ {
		wg.Add(1)
		go task(&wg)
	}
	wg.Wait()
	println("main over...")
}

运行结果如下:

3.最大并发数限制:

    多次运行仍然时main over在最后打印,代表了main协程最后执行完毕。虽然300个协程都去执行了,但是大量协程并发执行的时候,有时候效率并不高,例如每个任务运行的时候调用了别的服务的API,太大量的并发调用会导致别的服务崩溃或者响应越来越慢等问题,因此最大并发数的限制也是必要的,例如我们限制最多50个task在执行:

package main

import (
	"fmt"
	"sync"
	"time"
)

func task(wg *sync.WaitGroup, sema chan int) {
	defer wg.Done()
	defer func() {
		<-sema
	}()
	time.Sleep(time.Second * 2)
	fmt.Println("task...")
}

func main() {
	var wg sync.WaitGroup
	startTime := time.Now().Second()
	sema := make(chan int, 50)
	for i := 0; i < 300; i++ {
		wg.Add(1)
		sema <- i
		go task(&wg, sema)
	}
	wg.Wait()
	fmt.Printf("task token time %d s \n", time.Now().Second()-startTime)
	println("main over...")
}

 运行结果如下:

 单个任务是耗时2秒钟(因为做了2秒的睡眠),如果300个一起执行,300个全部运行结束耗时是2秒,但是限制最大并发数为50后,耗时是12秒。

4.相同类型结果的获取

现在限制了最大并发数后还有一个问题,如果我要获取每个协程的返回数据,且数据都是同一类型的,那么我们可以把每个协程的结果放入同一个缓冲通道中:

package main

import (
	"fmt"
	"sync"
	"time"
)

func task(wg *sync.WaitGroup, sema chan int, param int, taskResult chan int) {
	defer wg.Done()
	defer func() {
		<-sema
	}()
	time.Sleep(time.Second * 2)
	fmt.Println("task...")
	taskResult <- param
}

func main() {
	var wg sync.WaitGroup
	startTime := time.Now().Second()
	sema := make(chan int, 50)
	taskResult := make(chan int, 300) //长度为结果总数
	for i := 0; i < 300; i++ {
		wg.Add(1)
		sema <- i
		go task(&wg, sema, i, taskResult)
	}
	wg.Wait()
	fmt.Printf("task token time %d s \n", time.Now().Second()-startTime)
	close(taskResult)
	for v := range taskResult {
		fmt.Printf("结果 v= %d\n", v)
	}
	println("main over...")

运行结果如下:

注意:存储结果的缓冲通道 用for range的方式遍历获取的时候记得要关闭通道,否则会报错。

原因是通道未关闭的时候,如果已经没有元素在通道中的话,仍然从通道中获取,那么main协程会一直等待别的协程给通道加入元素,形成了死锁。当然也可以用一种for index的方式获取,那么就不用关闭通道,如下:

运行结果不会报错:

 

但是用for index的方式遍历结果时要注意,跳出条件的判断不能直接使用i<len(chin),因为每次从通道中取出一个元素之后,len(chan)的结果就减少一个,因此我们只能取到通道的一半数据。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值