Skip to content

简介

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)
}

输出:

1
2
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")
}

输出:

1
2
3
4
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())
    }
}

输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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)
}

输出:

1
2
readOps: 83233
writeOps: 8330