简介
Go语言最重要的一个特性就是原生支持并发,这对于开发高性能的Web应用是至关重要的。
一般来说,由于并发的程序运行顺序具有随机性(不确定性),每次执行的结果都有可能不同,因此多并发的程序往往难以开发,且难以调试。
有不少Bug在开发阶段都不会暴露,或者在上线运行一段时间后也不会暴露,只会在特定条件(比如并发量超过一定的阈值)才偶尔出现异常。
在并发状况下,为了保证共享数据(变量)的线程被安全访问,一般会通过加锁的方式(当然也可以通过无锁方式实现线程安全)来实现。
在Go语言中,并发编程的语法非常简洁,而且可以通过信道(Channel)机制来实现不同的协程(Goroutine)之间的通信。
Go语言被比作21世纪的C语言,首先是由于Go语言设计非常简单,其次是21世纪最重要的程序需求就是对并发特性的支持,而Go语言原生支持并发。
Go语言中的协程是非常轻量级的(相对于线程而言),普通的计算机上可以同时开启成千上万个协程。
timers
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 | package main
import (
"fmt"
"time"
)
func main() {
timer1 := time.NewTimer(2 * time.Second)
<-timer1.C
fmt.Println("Timer 1 fired")
timer2 := time.NewTimer(time.Second)
go func() {
<-timer2.C
fmt.Println("Timer 2 fired")
}()
stop2 := timer2.Stop()
if stop2 {
fmt.Println("Timer 2 stopped")
}
time.Sleep(2 * time.Second)
}
|
输出:
| Timer 1 fired
Timer 2 stopped
|
tickers
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 | package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(500 * time.Millisecond)
done := make(chan bool)
go func() {
for {
select {
case <-done:
return
case t := <-ticker.C:
fmt.Println("Tick at", t)
}
}
}()
time.Sleep(1600 * time.Millisecond)
ticker.Stop()
done <- true
fmt.Println("Ticker stopped")
}
|
输出:
| Tick at 2021-09-23 11:28:56.296372 +0800 CST m=+0.502888785
Tick at 2021-09-23 11:28:56.796369 +0800 CST m=+1.002877724
Tick at 2021-09-23 11:28:57.296422 +0800 CST m=+1.502921801
Ticker stopped
|
Rate Limiting
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 | package main
import (
"fmt"
"time"
)
func main() {
requests := make(chan int, 5)
for i := 1; i <= 5; i++ {
requests <- i
}
close(requests)
limiter := time.Tick(200 * time.Millisecond)
for req := range requests {
<-limiter
fmt.Println("request", req, time.Now())
}
burstyLimiter := make(chan time.Time, 3)
for i := 0; i < 3; i++ {
burstyLimiter <- time.Now()
}
go func() {
for t := range time.Tick(200 * time.Millisecond) {
burstyLimiter <- t
}
}()
burstyRequests := make(chan int, 5)
for i := 1; i <= 5; i++ {
burstyRequests <- i
}
close(burstyRequests)
for req := range burstyRequests {
<-burstyLimiter
fmt.Println("request", req, time.Now())
}
}
|
输出:
| request 1 2021-09-23 14:00:44.459784 +0800 CST m=+0.200573630
request 2 2021-09-23 14:00:44.659586 +0800 CST m=+0.400372538
request 3 2021-09-23 14:00:44.860809 +0800 CST m=+0.601592426
request 4 2021-09-23 14:00:45.059608 +0800 CST m=+0.800388546
request 5 2021-09-23 14:00:45.259998 +0800 CST m=+1.000776435
request 1 2021-09-23 14:00:45.260207 +0800 CST m=+1.000985722
request 2 2021-09-23 14:00:45.260302 +0800 CST m=+1.001079832
request 3 2021-09-23 14:00:45.260364 +0800 CST m=+1.001142548
request 4 2021-09-23 14:00:45.46041 +0800 CST m=+1.201184975
request 5 2021-09-23 14:00:45.661498 +0800 CST m=+1.402271095
|
Stateful Goroutines
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 | package main
import (
"fmt"
"math/rand"
"sync/atomic"
"time"
)
type readOp struct {
key int
resp chan int
}
type writeOp struct {
key int
val int
resp chan bool
}
func main() {
var readOps uint64
var writeOps uint64
reads := make(chan readOp)
writes := make(chan writeOp)
go func() {
var state = make(map[int]int)
for {
select {
case read := <-reads:
read.resp <- state[read.key]
case write := <-writes:
state[write.key] = write.val
write.resp <- true
}
}
}()
for r := 0; r < 100; r++ {
go func() {
for {
read := readOp{
key: rand.Intn(5),
resp: make(chan int)}
reads <- read
<-read.resp
atomic.AddUint64(&readOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
for w := 0; w < 10; w++ {
go func() {
for {
write := writeOp{
key: rand.Intn(5),
val: rand.Intn(100),
resp: make(chan bool)}
writes <- write
<-write.resp
atomic.AddUint64(&writeOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
time.Sleep(time.Second)
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
}
|
输出:
| readOps: 83233
writeOps: 8330
|