并发与并行
concurrncy和parallellism
并发指多个任务在同一时间段进行,并行指多个任务在同一时刻进行
单核cpu只能并发不能并行,现在的多核cpu既能并发又能并行
协程
coroutine,协程运行在线程上,一个线程可以拥有多个协程,协程只在用户态运行,由用户调度,它不像线程一样,由操作系统调度,切换涉及用户态和内核态,所以协程的开销很小。线程是栈MB级别,协程师栈KB级别
go使用协程
例子:启用5个协程,总共打印数字0~5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package main
import (
"fmt"
"time"
)
func hello(i int) {
println("hello", fmt.Sprint(i))
}
func main() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j)
}(i)
}
time.Sleep(time.Second) //为了避免子协程还没运行完,主协程先执行完了
}
|
协程间通信
协程间通信有两种:通道,共享内存(临界区)
一般通过通道更好,用临界区容易产生数据竞争
channel
通道有两种
无缓冲通道,a = make(chan int)
有缓冲通道,b = make(chan int, 2)
任务:A协程发送0~9数字,B协程得到数字计算数字的平方,主协程输出数字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package main
func main() {
channel1 := make(chan int)
channel2 := make(chan int, 3)
go func() {
defer close(channel1)
for i := 0; i < 10; i++ {
channel1 <- i
}
}()
go func() {
defer close(channel2)
for i := range channel1 {
channel2 <- i * i
}
}()
for i := range channel2 {
println(i, " ")
}
}
|
输出,可以发现输出是有序的,因为第2个通道是缓冲通道
共享内存,互斥锁
任务:开启5个协程,每个协程对某个变量执行2000次递增操作,分为加锁和不加锁情况
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
|
package main
import (
"sync"
"time"
)
var (
x int64
lock sync.Mutex
)
func addWithoutLock() {
for i := 0; i < 2000; i++ {
x++
}
}
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock()
x++
lock.Unlock()
}
}
func main() {
x = 0
for i := 0; i < 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
println("add without lock ", x)
x = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
println("add with lock ", x)
}
|
输出结果
WaitGroup
在上面的程序中使用time.Sleep的形式十分的不优雅,此处使用WaitGroup来让主协程等待子协程
WaitGroup使用计数器来达到要求,开启一个子协程时,计数器+1,结束一个子协程时,计数器-1,主协程会阻塞直到计数器为0。WaitGroup有三个重要的方法
Add方法计数器增加某个值,Done方法计数器-1,Wait方法阻塞当前协程,直到计数器为0
现在用WaitGroup来改写5个协程打印0~5的程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package main
import (
"fmt"
"sync"
)
func hello(i int) {
println("hello", fmt.Sprint(i))
}
func main() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done()
hello(j)
}(i)
}
wg.Wait()
}
|
使用M个协程按顺序交替打印N个数字
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
|
package main
import (
"fmt"
"sync"
)
func printN(m, n int) {
chs := make([]chan int, m)
for i := 0; i < m; i++ {
chs[i] = make(chan int)
}
var wg sync.WaitGroup
wg.Add(m)
for i := 0; i < m; i++ {
go func(id int) {
defer wg.Done()
for j := id + 1; j <= n; j += m {
x := <-chs[id]
fmt.Printf("goroutine: %d, number %d\n", id, x)
if x < n {
chs[(id+1)%m] <- x + 1
}
}
}(i)
}
chs[0] <- 1
wg.Wait()
}
func main() {
printN(4, 30)
}
|
输出
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
|
goroutine: 0, number 1
goroutine: 1, number 2
goroutine: 2, number 3
goroutine: 3, number 4
goroutine: 0, number 5
goroutine: 1, number 6
goroutine: 2, number 7
goroutine: 3, number 8
goroutine: 0, number 9
goroutine: 1, number 10
goroutine: 2, number 11
goroutine: 3, number 12
goroutine: 0, number 13
goroutine: 1, number 14
goroutine: 2, number 15
goroutine: 3, number 16
goroutine: 0, number 17
goroutine: 1, number 18
goroutine: 2, number 19
goroutine: 3, number 20
goroutine: 0, number 21
goroutine: 1, number 22
goroutine: 2, number 23
goroutine: 3, number 24
goroutine: 0, number 25
goroutine: 1, number 26
goroutine: 2, number 27
goroutine: 3, number 28
goroutine: 0, number 29
goroutine: 1, number 30
|