原文地址
没有使用锁时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package main

/*
https://www.cnblogs.com/zisefeizhu/p/15438992.html
*/
import (
"fmt"
"sync"
"time"
)

func httpGet(url int, response chan string, rep chan string, limiter chan bool, wg *sync.WaitGroup) {
// 函数执行完毕时 计数器-1
defer wg.Done()
// 将拿到的结果, 发送到参数中传递过来的channel中
response <- fmt.Sprintf("http get: %d", url)
time.Sleep(time.Second * 5)
rep <- fmt.Sprintf("rep get: %d", url)
// 释放一个坑位
<-limiter
}

// 将所有的返回结果, 以 []string 的形式返回
func collect(urls []int) ([]string, []string) {
var result []string
var tworesult []string
wg := &sync.WaitGroup{}
// 控制并发数为10
limiter := make(chan bool, 5)
defer close(limiter)

// 函数内的局部变量channel, 专门用来接收函数内所有goroutine的结果
responseChannel := make(chan string, 20)
repChannel := make(chan string, 20)
// 为读取结果控制器创建新的WaitGroup, 需要保证控制器内的所有值都已经正确处理完毕, 才能结束
wgResponse := &sync.WaitGroup{}
// 启动读取结果的控制器

// wgResponse计数器+2
wgResponse.Add(2)
go func() {
// 读取结果
for response := range responseChannel {
// 处理结果
fmt.Println("test01")
result = append(result, response)
}
// 当 responseChannel被关闭时且channel中所有的值都已经被处理完毕后, 将执行到这一行
wgResponse.Done()
}()
go func() {
// 读取结果
for rep := range repChannel {
// 处理结果
fmt.Println("test02")
tworesult = append(tworesult, rep)
}
// 当 responseChannel被关闭时且channel中所有的值都已经被处理完毕后, 将执行到这一行
wgResponse.Done()
}()
for _, url := range urls {
// 计数器+1
wg.Add(1)
limiter <- true
// 这里在启动goroutine时, 将用来收集结果的局部变量channel也传递进去
go httpGet(url, responseChannel, repChannel, limiter, wg)
}

// 等待所以协程执行完毕
wg.Wait() // 当计数器为0时, 不再阻塞
fmt.Println("所有协程已执行完毕")

// 关闭接收结果channel
close(responseChannel)
close(repChannel)

// 等待wgResponse的计数器归零
wgResponse.Wait()

// 返回聚合后结果
return result, tworesult
}

func main() {
urls := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result, tworesult := collect(urls)
fmt.Println(result)
fmt.Println(tworesult)
}


输出, test01和test02是有间隔的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
test01
test01
test01
test01
test01
test02
test02
test02
test02
test02
test01
test01
test01
test01
test01
test02
所有协程已执行完毕
test02
test02
test02
test02
[http get: 5 http get: 1 http get: 2 http get: 3 http get: 4 http get: 6 http get: 10 http get: 7 http get: 8 http get: 9]
[rep get: 1 rep get: 4 rep get: 5 rep get: 2 rep get: 3 rep get: 7 rep get: 8 rep get: 6 rep get: 9 rep get: 10]

go关键字在并发时候,for chan类似python的yield,go关键字类似asyncio事件循环
但是性能还是go好很多,相比python aiomultiprocess也可以做到类似效果