goroutine-看一个需求
- 需求:要求统计 1-9000000000 的数字中,哪些是素数?
- 分析思路:
- 传统的方法,就是使用一个循环,循环的判断各个数是不是素数。[很慢]
- 使用并发或者并行的方式,将统计素数的任务分配给多个 goroutine 去完成,这时就会使用到
goroutine.【速度提高 4 倍】
goroutine-基本介绍
进程和线程介绍
程序、进程和线程的关系示意图
并发和并行
- 多线程程序在单核上运行,就是并发
- 多线程程序在多核上运行,就是并行
- 示意图:
- 小结
Go 协程和 Go 主线程
Go 主线程(有程序员直接称为线程/也可以理解成进程): 一个 Go 线程上,可以起多个协程,你可以 这样理解,协程是轻量级的线程[编译器做优化]。
Go 协程的特点
- 有独立的栈空间
- 共享程序堆空间
- 调度由用户控制
- 协程是轻量级的线程
一个示意图
goroutine-快速入门
案例说明
请编写一个程序,完成如下功能:
- 在主线程(可以理解成进程)中,开启一个 goroutine, 该协程每隔 1 秒输出 “hello,world”
- 在主线程中也每隔一秒输出"hello,golang", 输出 10 次后,退出程序
- 要求主线程和 goroutine 同时执行.
- 画出主线程和协程执行流程图
代码实现
输出的效果说明, main 这个主线程和 test 协程同时执行
主线程和协程执行流程图
快速入门小结
- 主线程是一个物理线程,直接作用在 cpu 上的。是重量级的,非常耗费 cpu 资源。
- 协程从主线程开启的,是轻量级的线程,是逻辑态。对资源消耗相对小。
- Golang 的协程机制是重要的特点,可以轻松的开启上万个协程。其它编程语言的并发机制是一 般基于线程的,开启过多的线程,资源耗费大,这里就突显 Golang 在并发上的优势了
goroutine 的调度模型
MPG 模式基本介绍
MPG 模式运行的状态 1
MPG 模式运行的状态 2
设置 Golang 运行的 cpu 数
介绍:为了充分了利用多 cpu 的优势,在 Golang 程序中,设置运行的 cpu 数目
channel(管道)-看个需求
需求:现在要计算 1-200 的各个数的阶乘,并且把各个数的阶乘放入到 map 中。最后显示出来。 要求使用 goroutine 完成
分析思路:
- 使用 goroutine 来完成,效率高,但是会出现并发/并行安全问题.
- 这里就提出了不同 goroutine 如何通信的问题
代码实现
- 使用 goroutine 来完成(看看使用 gorotine 并发完成会出现什么问题? 然后我们会去解决)
- 在运行某个程序时,如何知道是否存在资源竞争问题。 方法很简单,在编译该程序时,增加一 个参数 -race 即可 [示意图]
- 代码实现:
package main
import (
"fmt"
"time"
)
// 需求:现在要计算 1-200 的各个数的阶乘,并且把各个数的阶乘放入到 map 中。
// 最后显示出来。要求使用 goroutine 完成
// 思路
// 1. 编写一个函数,来计算各个数的阶乘,并放入到 map 中.
// 2. 我们启动的协程多个,统计的将结果放入到 map 中
// 3. map 应该做出一个全局的.
var (
myMap = make(map[int]int, 10)
)
// test 函数就是计算 n!, 让将这个结果放入到myMap
func test(n int) {
res := 1
for i := 1; i <= n; i++ {
res *= i
}
//这里我们将 res 放入到 myMap
myMap[n] = res //concurrent map writes?
}
func main() {
// 我们这里开启多个协程完成这个任务[200 个]
for i := 1; i <= 200; i++ {
go test(i)
}
//休眠 10 秒钟【第二个问题 】
time.Sleep(time.Second * 10)
//这里我们输出结果,变量这个结果
for i, v := range myMap {
fmt.Printf("map[%d]=%d\n", i, v)
}
}
4) 示意图:
不同 goroutine 之间如何通讯
- 全局变量的互斥锁
- 使用管道 channel 来解决
使用全局变量加锁同步改进程序
- 因为没有对全局变量 m 加锁,因此会出现资源争夺问题,代码会出现错误,提示 concurrent map writes
- 解决方案:加入互斥锁
- 我们的数的阶乘很大,结果会越界,可以将求阶乘改成 sum += uint64(i)
- 代码改进
package main
import (
"fmt"
"time"
"sync"
)
// 需求:现在要计算 1-200 的各个数的阶乘,并且把各个数的阶乘放入到 map 中。
// 最后显示出来。要求使用 goroutine 完成
// 思路
// 1. 编写一个函数,来计算各个数的阶乘,并放入到 map 中.
// 2. 我们启动的协程多个,统计的将结果放入到 map 中
// 3. map 应该做出一个全局的.
var (
myMap = make(map[int]int, 10)
//声明一个全局变量
//lock 是一个全局的互斥锁
//sync 同步
//Mutex 互斥
lock sync.Mutex
)
// test 函数就是计算 n!, 让将这个结果放入到myMap
func test(n int) {
res := 1
for i := 1; i <= n; i++ {
res *= i
}
//这里我们将 res 放入到 myMap
//枷锁
lock.Lock()
myMap[n] = res //concurrent map writes?
//开锁
lock.Unlock()
}
func main() {
// 我们这里开启多个协程完成这个任务[200 个]
for i := 1; i <= 200; i++ {
go test(i)
}
//休眠 10 秒钟【第二个问题 】
time.Sleep(time.Second * 10)
lock.Lock()
//这里我们输出结果,变量这个结果
for i, v := range myMap {
fmt.Printf("map[%d]=%d\n", i, v)
}
lock.Unlock()
}