需求
计算 1 - 200 各个数的阶乘,然后存放到map中, 并输出
代码
// channel 引出
package main
import (
"fmt"
"time"
)
// 计算 1 - 200 各个数的阶乘,然后存放到map中, 并输出
// 思路
// 1、编写一个函数 计算各个数的阶层,并放入到 map 中
// 2、启动多个协程,将统计结果放入 map 中
// 3、map 应该是全局的
var (
myMap = make(map[int]int, 10)
)
// 编写计算阶乘的函数,并将结果放入 map
func test(n int) {
res := 1
for i := 1; i <= n; i++ {
res *= i
}
// 放入 map
myMap[n] = res
}
func main() {
for i := 0; i < 200; i++ {
go test(i)
}
// 在这里 休眠 10s 中 ?, 尝试等待协程运行结束
time.Sleep(10 * time.Second)
// 遍历输出结果 主线程瞬间结束,程序退出,无输出
for i, v := range myMap {
fmt.Printf("myMap[%v]=%v \n", i, v)
}
}
问题
fatal error: concurrent map writes
Map,对于共享变量,资源,并发写会产生竞争, 共享资源遭到破坏,
命令查看资源是否存在竞争 -race
go build -race main.go // 得到 main.exe
main.exe // 运行 main.exe
// 输出:
...
myMap[161]=0
myMap[170]=0
myMap[191]=0
myMap[193]=0
myMap[195]=0
myMap[149]=0
myMap[154]=0
myMap[86]=0
myMap[104]=0
myMap[110]=0
myMap[124]=0
myMap[178]=0
myMap[180]=0
myMap[19]=121645100408832000
myMap[82]=0
myMap[187]=0
myMap[194]=0
myMap[152]=0
myMap[153]=0
myMap[51]=-162551799050403840
myMap[53]=-5270900413883744256
myMap[106]=0
myMap[176]=0
myMap[181]=0
myMap[22]=-1250660718674968576
myMap[32]=-6045878379276664832
Found 2 data race(s) // 有两个资源存在竞争
不同goroutine之间如何通讯
使用全局变量加锁,同步改进程序
因为没有对全局变量 m 加锁,因此会出现资源争夺问题,代码会出现错误提示: concurrent map writes
解决方案:加入互斥锁
map 加锁,没有关闭则排队等待
协程goroutine
持有锁——lock
|
map空间
|
释放锁——unlock
队列——排队等待
import “sync”
sync包提供了基本的同步基元,如互斥锁。除了Once和WaitGroup类型,大部分都是适用于低水平程序线程,高水平的同步使用channel通信更好一些。
代码
// channel 引出
package main
import (
"fmt"
"sync"
"time"
)
// 计算 1 - 200 各个数的阶乘,然后存放到map中, 并输出
// 思路
// 1、编写一个函数 计算各个数的阶层,并放入到 map 中
// 2、启动多个协程,将统计结果放入 map 中
// 3、map 应该是全局的
var (
myMap = make(map[int]int, 10)
// 声明一个全局互斥锁
lock sync.Mutex
)
// 编写计算阶乘的函数,并将结果放入 map
func test(n int) {
res := 1
for i := 1; i <= n; i++ {
res *= i
}
// 操作 map 前加锁
lock.Lock()
// 放入 map
myMap[n] = res
// 操作完之后解锁
lock.Unlock()
}
func main() {
for i := 0; i < 20; i++ {
go test(i)
}
// 休眠 10s 中 ?
time.Sleep(10 * time.Second)
// 遍历输出结果 主线程瞬间结束,程序退出,无输出
lock.Lock()
for i, v := range myMap {
fmt.Printf("myMap[%v]=%v \n", i, v)
}
lock.Unlock()
}
问题
- 上面的主线程 休眠 10 秒,去等待协程执行完毕。如果时间设置的长了,协程已经执行完毕,就会造成资源的浪费;如果时间设置的短了,协程不执行完毕主线程就退出了。不够完美,需要进一步改进。
- 全局变量加锁同步来实现通讯,不利于多个协程对全局变量的读写操作
引出新的通讯机制: channel