并发状态

  • 共享值
  • 竞争条件(race condition)

Go 的互斥锁(mutex)

  • mutex = mutual exclusive
  • Lock(),Unlock()
  • sync 包
  1. package main
  2. import "sync"
  3. var mu sync.Mutex
  4. func main() {
  5. mu.Lock()
  6. defer mu.Unlock()
  7. // The lock is held until we return from the function.
  8. }
  • 互斥锁定义在被保护的变量之上
  1. package main
  2. import "sync"
  3. // Visited tracks whether web pages have been visited.
  4. // Its methods may be used concurrently with one another.
  5. type Visited struct {
  6. // mu guards the visited map.
  7. mu sync.Mutex
  8. visited map[string]int
  9. }
  10. // VisitLink tracks that the page with the given URL has
  11. // been visited, and returns the updated link count.
  12. func (v *Visited) VisitLink(url string) int {
  13. v.mu.Lock()
  14. defer v.mu.Unlock()
  15. count := v.visited[url]
  16. count++
  17. v.visited[url] = count
  18. return count
  19. }
  20. func main() {
  21. }

互斥锁的隐患

  • 死锁
  • 为保证互斥锁的安全使用,我们须遵守以下规则:
    • 尽可能的简化互斥锁保护的代码
    • 对每一份共享状态只使用一个互斥锁

长时间运行的工作进程

  • 工作进程(worker)
    • 通常会被写成包含 select 语句的 for 循环。
  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func main() {
  7. go worker()
  8. time.Sleep(5 * time.Second)
  9. }
  10. func worker() {
  11. n := 0
  12. next := time.After(time.Second)
  13. for {
  14. select {
  15. case <-next:
  16. n++
  17. fmt.Println(n)
  18. next = time.After(time.Second)
  19. }
  20. }
  21. }
  1. package main
  2. import (
  3. "fmt"
  4. "image"
  5. "time"
  6. )
  7. func main() {
  8. go worker()
  9. time.Sleep(5 * time.Second)
  10. }
  11. func worker() {
  12. pos := image.Point{X: 10, Y: 10}
  13. direction := image.Point{X: 1, Y: 0}
  14. next := time.After(time.Second)
  15. for {
  16. select {
  17. case <-next:
  18. pos = pos.Add(direction)
  19. fmt.Println("current position is ", pos)
  20. next = time.After(time.Second)
  21. }
  22. }
  23. }
  1. package main
  2. import (
  3. "image"
  4. "log"
  5. "time"
  6. )
  7. func main() {
  8. r := NewRoverDriver()
  9. time.Sleep(3 * time.Second)
  10. r.Left()
  11. time.Sleep(3 * time.Second)
  12. r.Right()
  13. time.Sleep(3 * time.Second)
  14. }
  15. // RoverDriver drives a rover around the surface of Mars.
  16. type RoverDriver struct {
  17. commandc chan command
  18. }
  19. // NewRoverDriver starts a new RoverDriver and returns it.
  20. func NewRoverDriver() *RoverDriver {
  21. r := &RoverDriver{
  22. commandc: make(chan command),
  23. }
  24. go r.drive()
  25. return r
  26. }
  27. type command int
  28. const (
  29. right = command(0)
  30. left = command(1)
  31. )
  32. // drive is responsible for driving the rover. It
  33. // is expected to be started in a goroutine.
  34. func (r *RoverDriver) drive() {
  35. pos := image.Point{X: 0, Y: 0}
  36. direction := image.Point{X: 1, Y: 0}
  37. updateInterval := 250 * time.Millisecond
  38. nextMove := time.After(updateInterval)
  39. for {
  40. select {
  41. case c := <-r.commandc:
  42. switch c {
  43. case right:
  44. direction = image.Point{
  45. X: -direction.Y,
  46. Y: direction.X,
  47. }
  48. case left:
  49. direction = image.Point{
  50. X: direction.Y,
  51. Y: -direction.X,
  52. }
  53. }
  54. log.Printf("new direction %v", direction)
  55. case <-nextMove:
  56. pos = pos.Add(direction)
  57. log.Printf("moved to %v", pos)
  58. nextMove = time.After(updateInterval)
  59. }
  60. }
  61. }
  62. // Left turns the rover left (90° counterclockwise).
  63. func (r *RoverDriver) Left() {
  64. r.commandc <- left
  65. }
  66. // Right turns the rover right (90° clockwise).
  67. func (r *RoverDriver) Right() {
  68. r.commandc <- right
  69. }

事件循环和 goroutine

  • 事件循环(event loop)
  • 中心循环(central loop)
  • Go 通过提供 goroutine 作为核心概念,消除了对中心循环的需求。

作业题

  • 以例子 31.5 为基础,修改代码使得每次移动之间的间隔增加半秒。
  1. package main
  2. import (
  3. "fmt"
  4. "image"
  5. "time"
  6. )
  7. func main() {
  8. go worker()
  9. time.Sleep(5 * time.Second)
  10. }
  11. func worker() {
  12. pos := image.Point{X: 10, Y: 10}
  13. direction := image.Point{X: 1, Y: 0}
  14. delay := time.Second
  15. next := time.After(delay)
  16. for {
  17. select {
  18. case <-next:
  19. pos = pos.Add(direction)
  20. fmt.Println("current position is ", pos)
  21. delay += time.Second / 2
  22. next = time.After(delay)
  23. }
  24. }
  25. }
  • 以 RoverDriver 类型为基础,定义 Start 方法、Stop 方法和对应的命令,然后修改代码使得探测器可以接受这两个新命令。
  1. package main
  2. import (
  3. "image"
  4. "log"
  5. "time"
  6. )
  7. func main() {
  8. r := NewRoverDriver()
  9. time.Sleep(3 * time.Second)
  10. r.Left()
  11. time.Sleep(3 * time.Second)
  12. r.Right()
  13. time.Sleep(3 * time.Second)
  14. r.Stop()
  15. time.Sleep(3 * time.Second)
  16. r.Start()
  17. time.Sleep(3 * time.Second)
  18. }
  19. // RoverDriver drives a rover around the surface of Mars.
  20. type RoverDriver struct {
  21. commandc chan command
  22. }
  23. // NewRoverDriver starts a new RoverDriver and returns it.
  24. func NewRoverDriver() *RoverDriver {
  25. r := &RoverDriver{
  26. commandc: make(chan command),
  27. }
  28. go r.drive()
  29. return r
  30. }
  31. type command int
  32. const (
  33. right = command(0)
  34. left = command(1)
  35. start = command(2)
  36. stop = command(3)
  37. )
  38. // drive is responsible for driving the rover. It
  39. // is expected to be started in a goroutine.
  40. func (r *RoverDriver) drive() {
  41. pos := image.Point{X: 0, Y: 0}
  42. direction := image.Point{X: 1, Y: 0}
  43. updateInterval := 250 * time.Millisecond
  44. nextMove := time.After(updateInterval)
  45. speed := 1
  46. for {
  47. select {
  48. case c := <-r.commandc:
  49. switch c {
  50. case right:
  51. direction = image.Point{
  52. X: -direction.Y,
  53. Y: direction.X,
  54. }
  55. case left:
  56. direction = image.Point{
  57. X: direction.Y,
  58. Y: -direction.X,
  59. }
  60. case stop:
  61. speed = 0
  62. case start:
  63. speed = 1
  64. }
  65. log.Printf("new direction %v; speed %d", direction, speed)
  66. case <-nextMove:
  67. pos = pos.Add(direction.Mul(speed))
  68. log.Printf("moved to %v", pos)
  69. nextMove = time.After(updateInterval)
  70. }
  71. }
  72. }
  73. // Left turns the rover left (90° counterclockwise).
  74. func (r *RoverDriver) Left() {
  75. r.commandc <- left
  76. }
  77. // Right turns the rover right (90° clockwise).
  78. func (r *RoverDriver) Right() {
  79. r.commandc <- right
  80. }
  81. // Stop halts the rover.
  82. func (r *RoverDriver) Stop() {
  83. r.commandc <- stop
  84. }
  85. // Start gets the rover moving.
  86. func (r *RoverDriver) Start() {
  87. r.commandc <- start
  88. }