不会飞的章鱼

熟能生巧,勤能补拙;念念不忘,必有回响。

Go语言精进——优先考虑并发设计

并发与并行

并行方案

在处理器核数充足的情况下启动多个单线程应用的实例,这样每个实例运行在一个核上,尽可能多地利用多核计算资源。

并发方案

重新做应用结构设计,即将应用分解成多个在基本执行单元中执行的、可能有一定关联关系的代码片段。

goroutine相比线程具有的优势

  • 资源占用小,每个goroutine的初始栈大小为2KB
  • 由Go运行时而不是操作系统调度,goroutine上下文切换代价较小
  • 语言原生支持
  • 内置channel作为goroutine之间的通信原语,为并发设计提供强大支撑

Go并发设计实例

模拟机场安检

第一版:顺序设计

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
const (
idCheckTmCost = 60
bodyCheckTmCost = 120
xRayCheckTmCost = 180
)

func idCheck() int {
time.Sleep(time.Millisecond * time.Duration(idCheckTmCost))
println("\tidCheck ok")
return idCheckTmCost
}

func bodyCheck() int {
time.Sleep(time.Millisecond * time.Duration(bodyCheckTmCost))
println("\tbodyCheck ok")
return bodyCheckTmCost
}

func xRayCheck() int {
time.Sleep(time.Millisecond * time.Duration(xRayCheckTmCost))
println("\txRayCheck ok")
return xRayCheckTmCost
}

func airportSecurityCheck() int {
println("airportSecurityCheck ...")
total := 0

total += idCheck()
total += bodyCheck()
total += xRayCheck()

println("airportSecurityCheck ok")
return total
}

func main() {
total := 0
passengers := 30
for i := 0; i < passengers; i++ {
total += airportSecurityCheck()
}
println("total time cost:", total)
}

// total time cost: 10800

第二版:并行方案

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
const (
idCheckTmCost = 60
bodyCheckTmCost = 120
xRayCheckTmCost = 180
)

func idCheck(id int) int {
time.Sleep(time.Millisecond * time.Duration(idCheckTmCost))
print("\tgoroutine-", id, ": idCheck ok\n")
return idCheckTmCost
}

func bodyCheck(id int) int {
time.Sleep(time.Millisecond * time.Duration(bodyCheckTmCost))
print("\tgoroutine-", id, ": bodyCheck ok\n")
return bodyCheckTmCost
}

func xRayCheck(id int) int {
time.Sleep(time.Millisecond * time.Duration(xRayCheckTmCost))
print("\tgoroutine-", id, ": xRayCheck ok\n")
return xRayCheckTmCost
}

func airportSecurityCheck(id int) int {
print("goroutine-", id, ": airportSecurityCheck ...\n")
total := 0

total += idCheck(id)
total += bodyCheck(id)
total += xRayCheck(id)

print("goroutine-", id, ": airportSecurityCheck ok\n")
return total
}

func start(id int, f func(int) int, queue <-chan struct{}) <-chan int {
c := make(chan int)
go func() {
total := 0
for {
_, ok := <-queue
if !ok {
c <- total
return
}
total += f(id)
}
}()
return c
}

func max(args ...int) int {
n := 0
for _, v := range args {
if v > n {
n = v
}
}
return n
}

func main() {
total := 0
passengers := 30
c := make(chan struct{})
c1 := start(1, airportSecurityCheck, c)
c2 := start(2, airportSecurityCheck, c)
c3 := start(3, airportSecurityCheck, c)

for i := 0; i < passengers; i++ {
c <- struct{}{}
}
close(c)

total = max(<-c1, <-c2, <-c3)
println("total time cost:", total)
}

// total time cost: 3600

第三版:并发方案

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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
const (
idCheckTmCost = 60
bodyCheckTmCost = 120
xRayCheckTmCost = 180
)

func idCheck(id string) int {
time.Sleep(time.Millisecond * time.Duration(idCheckTmCost))
print("\tgoroutine-", id, "-idCheck: idCheck ok\n")
return idCheckTmCost
}

func bodyCheck(id string) int {
time.Sleep(time.Millisecond * time.Duration(bodyCheckTmCost))
print("\tgoroutine-", id, "-bodyCheck: bodyCheck ok\n")
return bodyCheckTmCost
}

func xRayCheck(id string) int {
time.Sleep(time.Millisecond * time.Duration(xRayCheckTmCost))
print("\tgoroutine-", id, "-xRayCheck: xRayCheck ok\n")
return xRayCheckTmCost
}

func start(id string, f func(string) int, next chan<- struct{}) (chan<- struct{}, chan<- struct{}, <-chan int) {
queue := make(chan struct{}, 10)
quit := make(chan struct{})
result := make(chan int)

go func() {
total := 0
for {
select {
case <-quit:
result <- total
return
case v := <-queue:
total += f(id)
if next != nil {
next <- v
}
}
}

}()
return queue, quit, result
}

func newAirportSecurityCheckChannel(id string, queue <-chan struct{}) {
go func(id string) {
print("goroutine-", id, ": airportSecurityCheckChannel is ready...\n")
// start xRayCheck routine
queue3, quit3, result3 := start(id, xRayCheck, nil)

// start bodyCheck routine
queue2, quit2, result2 := start(id, bodyCheck, queue3)

// start idCheck routine
queue1, quit1, result1 := start(id, idCheck, queue2)

for {
select {
case v, ok := <-queue:
if !ok {
close(quit1)
close(quit2)
close(quit3)
total := max(<-result1, <-result2, <-result3)
print("goroutine-", id, ": airportSecurityCheckChannel time cost:", total, "\n")
print("goroutine-", id, ": airportSecurityCheckChannel closed\n")
return
}
queue1 <- v
}
}
}(id)
}

func max(args ...int) int {
n := 0
for _, v := range args {
if v > n {
n = v
}
}
return n
}

func main() {
passengers := 30
queue := make(chan struct{}, 30)
newAirportSecurityCheckChannel("channel1", queue)
newAirportSecurityCheckChannel("channel2", queue)
newAirportSecurityCheckChannel("channel3", queue)

time.Sleep(5 * time.Second) // 保证上述三个goroutine都已经处于ready状态
for i := 0; i < passengers; i++ {
queue <- struct{}{}
}
time.Sleep(5 * time.Second)
close(queue) // 为了打印各通道的处理时长
time.Sleep(1000 * time.Second)
}

/*
goroutine-channel2: airportSecurityCheckChannel time cost:2160
goroutine-channel1: airportSecurityCheckChannel time cost:1080
goroutine-channel3: airportSecurityCheckChannel time cost:2160
*/
------ 本文结束------
如果本篇文章对你有帮助,可以给作者加个鸡腿~(*^__^*),感谢鼓励与支持!