特点
- go主线程是一个物理线程,直接作用在CPU上,重量级,非常耗费CPU资源
- 协程是从主线程开启的,是轻量级的线程,是逻辑态,资源消耗相对较小
- go可以轻松开启上万个协程
MPG
- M: 操作系统的主线程
- P: 协程执行需要的上下文环境
- G: 协程
当多个主线程运行在一个CPU核中,认为该状态为并发
当多个主线程运行在多个CPU核中,认为该状态为并行
使用
- 支持使用
runtime.GOMAXPROCS
方法设置使用的CPU核数
package main
import (
"fmt"
"strconv"
"time"
)
func main() {
// 启动协程
go test()
// 继续主线程
for i := 1; i <= 10; i++ {
fmt.Println("main() ..." + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
func test() {
for i := 1; i <= 10; i++ {
fmt.Println("test() ..." + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
结果
main() ...1
test() ...1
test() ...2
main() ...2
test() ...3
main() ...3
main() ...4
test() ...4
test() ...5
main() ...5
main() ...6
test() ...6
test() ...7
main() ...7
test() ...8
main() ...8
main() ...9
test() ...9
main() ...10
test() ...10
资源竞争
多线程计算阶乘
使用锁解决
package main
import (
"fmt"
"sync"
"time"
)
var (
myMap = make(map[int64]int64, 10)
// 全局互斥锁
lock sync.Mutex
)
func Factorial(n int64) {
var res int64 = 1
var i int64
for i = 1; i <= n; i++ {
res *= i
}
lock.Lock()
myMap[n] = res
lock.Unlock()
}
func main() {
for i := 0; i < 20; i++ {
go Factorial(int64(i))
}
time.Sleep(time.Second * 10)
lock.Lock()
for k, v := range myMap {
fmt.Println(k, "=", v)
}
lock.Unlock()
}
结果
4 = 24
11 = 39916800
8 = 40320
10 = 3628800
2 = 2
12 = 479001600
13 = 6227020800
0 = 1
5 = 120
3 = 6
7 = 5040
14 = 87178291200
18 = 6402373705728000
1 = 1
6 = 720
9 = 362880
19 = 121645100408832000
17 = 355687428096000
15 = 1307674368000
16 = 20922789888000
使用
go build -race main.go
查看并发资源竞争
使用channel解决(推荐)
channel本质是一个线程安全的队列,channel是引用类型
管道的使用
// 使用:var 变量名 chan 队列内元素类型
var myChan chan int = make(chan int, 10)
// 向管道写入数据
myChan <- 10
// 从管道读取数据
var num2 int = <- myChan