需求

计算 1 - 200 各个数的阶乘,然后存放到map中, 并输出
image.png

代码

  1. // channel 引出
  2. package main
  3. import (
  4. "fmt"
  5. "time"
  6. )
  7. // 计算 1 - 200 各个数的阶乘,然后存放到map中, 并输出
  8. // 思路
  9. // 1、编写一个函数 计算各个数的阶层,并放入到 map 中
  10. // 2、启动多个协程,将统计结果放入 map 中
  11. // 3、map 应该是全局的
  12. var (
  13. myMap = make(map[int]int, 10)
  14. )
  15. // 编写计算阶乘的函数,并将结果放入 map
  16. func test(n int) {
  17. res := 1
  18. for i := 1; i <= n; i++ {
  19. res *= i
  20. }
  21. // 放入 map
  22. myMap[n] = res
  23. }
  24. func main() {
  25. for i := 0; i < 200; i++ {
  26. go test(i)
  27. }
  28. // 在这里 休眠 10s 中 ?, 尝试等待协程运行结束
  29. time.Sleep(10 * time.Second)
  30. // 遍历输出结果 主线程瞬间结束,程序退出,无输出
  31. for i, v := range myMap {
  32. fmt.Printf("myMap[%v]=%v \n", i, v)
  33. }
  34. }

问题

image.png

  1. fatal error: concurrent map writes

Map,对于共享变量,资源,并发写会产生竞争, 共享资源遭到破坏,

命令查看资源是否存在竞争 -race

  1. go build -race main.go // 得到 main.exe
  2. main.exe // 运行 main.exe
  3. // 输出:
  4. ...
  5. myMap[161]=0
  6. myMap[170]=0
  7. myMap[191]=0
  8. myMap[193]=0
  9. myMap[195]=0
  10. myMap[149]=0
  11. myMap[154]=0
  12. myMap[86]=0
  13. myMap[104]=0
  14. myMap[110]=0
  15. myMap[124]=0
  16. myMap[178]=0
  17. myMap[180]=0
  18. myMap[19]=121645100408832000
  19. myMap[82]=0
  20. myMap[187]=0
  21. myMap[194]=0
  22. myMap[152]=0
  23. myMap[153]=0
  24. myMap[51]=-162551799050403840
  25. myMap[53]=-5270900413883744256
  26. myMap[106]=0
  27. myMap[176]=0
  28. myMap[181]=0
  29. myMap[22]=-1250660718674968576
  30. myMap[32]=-6045878379276664832
  31. Found 2 data race(s) // 有两个资源存在竞争

不同goroutine之间如何通讯

1、全局变量加锁同步
2、channel

使用全局变量加锁,同步改进程序

因为没有对全局变量 m 加锁,因此会出现资源争夺问题,代码会出现错误提示: concurrent map writes
解决方案:加入互斥锁

map 加锁,没有关闭则排队等待
协程goroutine
持有锁——lock
|
map空间
|
释放锁——unlock
队列——排队等待
image.png

import “sync”

sync包提供了基本的同步基元,如互斥锁。除了Once和WaitGroup类型,大部分都是适用于低水平程序线程高水平的同步使用channel通信更好一些

代码

  1. // channel 引出
  2. package main
  3. import (
  4. "fmt"
  5. "sync"
  6. "time"
  7. )
  8. // 计算 1 - 200 各个数的阶乘,然后存放到map中, 并输出
  9. // 思路
  10. // 1、编写一个函数 计算各个数的阶层,并放入到 map 中
  11. // 2、启动多个协程,将统计结果放入 map 中
  12. // 3、map 应该是全局的
  13. var (
  14. myMap = make(map[int]int, 10)
  15. // 声明一个全局互斥锁
  16. lock sync.Mutex
  17. )
  18. // 编写计算阶乘的函数,并将结果放入 map
  19. func test(n int) {
  20. res := 1
  21. for i := 1; i <= n; i++ {
  22. res *= i
  23. }
  24. // 操作 map 前加锁
  25. lock.Lock()
  26. // 放入 map
  27. myMap[n] = res
  28. // 操作完之后解锁
  29. lock.Unlock()
  30. }
  31. func main() {
  32. for i := 0; i < 20; i++ {
  33. go test(i)
  34. }
  35. // 休眠 10s 中 ?
  36. time.Sleep(10 * time.Second)
  37. // 遍历输出结果 主线程瞬间结束,程序退出,无输出
  38. lock.Lock()
  39. for i, v := range myMap {
  40. fmt.Printf("myMap[%v]=%v \n", i, v)
  41. }
  42. lock.Unlock()
  43. }

问题

  1. 上面的主线程 休眠 10 秒,去等待协程执行完毕。如果时间设置的长了,协程已经执行完毕,就会造成资源的浪费;如果时间设置的短了,协程不执行完毕主线程就退出了。不够完美,需要进一步改进。
  2. 全局变量加锁同步来实现通讯,不利于多个协程对全局变量的读写操作

引出新的通讯机制: channel