并发状态
Go 的互斥锁(mutex)
- mutex = mutual exclusive
- Lock(),Unlock()
- sync 包
package mainimport "sync"var mu sync.Mutexfunc main() { mu.Lock() defer mu.Unlock() // The lock is held until we return from the function.}
package mainimport "sync"// Visited tracks whether web pages have been visited.// Its methods may be used concurrently with one another.type Visited struct { // mu guards the visited map. mu sync.Mutex visited map[string]int}// VisitLink tracks that the page with the given URL has// been visited, and returns the updated link count.func (v *Visited) VisitLink(url string) int { v.mu.Lock() defer v.mu.Unlock() count := v.visited[url] count++ v.visited[url] = count return count}func main() {}
互斥锁的隐患
- 死锁
- 为保证互斥锁的安全使用,我们须遵守以下规则:
- 尽可能的简化互斥锁保护的代码
- 对每一份共享状态只使用一个互斥锁
长时间运行的工作进程
- 工作进程(worker)
- 通常会被写成包含 select 语句的 for 循环。
package mainimport ( "fmt" "time")func main() { go worker() time.Sleep(5 * time.Second)}func worker() { n := 0 next := time.After(time.Second) for { select { case <-next: n++ fmt.Println(n) next = time.After(time.Second) } }}
package mainimport ( "fmt" "image" "time")func main() { go worker() time.Sleep(5 * time.Second)}func worker() { pos := image.Point{X: 10, Y: 10} direction := image.Point{X: 1, Y: 0} next := time.After(time.Second) for { select { case <-next: pos = pos.Add(direction) fmt.Println("current position is ", pos) next = time.After(time.Second) } }}
package mainimport ( "image" "log" "time")func main() { r := NewRoverDriver() time.Sleep(3 * time.Second) r.Left() time.Sleep(3 * time.Second) r.Right() time.Sleep(3 * time.Second)}// RoverDriver drives a rover around the surface of Mars.type RoverDriver struct { commandc chan command}// NewRoverDriver starts a new RoverDriver and returns it.func NewRoverDriver() *RoverDriver { r := &RoverDriver{ commandc: make(chan command), } go r.drive() return r}type command intconst ( right = command(0) left = command(1))// drive is responsible for driving the rover. It// is expected to be started in a goroutine.func (r *RoverDriver) drive() { pos := image.Point{X: 0, Y: 0} direction := image.Point{X: 1, Y: 0} updateInterval := 250 * time.Millisecond nextMove := time.After(updateInterval) for { select { case c := <-r.commandc: switch c { case right: direction = image.Point{ X: -direction.Y, Y: direction.X, } case left: direction = image.Point{ X: direction.Y, Y: -direction.X, } } log.Printf("new direction %v", direction) case <-nextMove: pos = pos.Add(direction) log.Printf("moved to %v", pos) nextMove = time.After(updateInterval) } }}// Left turns the rover left (90° counterclockwise).func (r *RoverDriver) Left() { r.commandc <- left}// Right turns the rover right (90° clockwise).func (r *RoverDriver) Right() { r.commandc <- right}
事件循环和 goroutine
- 事件循环(event loop)
- 中心循环(central loop)
- Go 通过提供 goroutine 作为核心概念,消除了对中心循环的需求。
作业题
- 以例子 31.5 为基础,修改代码使得每次移动之间的间隔增加半秒。
package mainimport ( "fmt" "image" "time")func main() { go worker() time.Sleep(5 * time.Second)}func worker() { pos := image.Point{X: 10, Y: 10} direction := image.Point{X: 1, Y: 0} delay := time.Second next := time.After(delay) for { select { case <-next: pos = pos.Add(direction) fmt.Println("current position is ", pos) delay += time.Second / 2 next = time.After(delay) } }}
- 以 RoverDriver 类型为基础,定义 Start 方法、Stop 方法和对应的命令,然后修改代码使得探测器可以接受这两个新命令。
package mainimport ( "image" "log" "time")func main() { r := NewRoverDriver() time.Sleep(3 * time.Second) r.Left() time.Sleep(3 * time.Second) r.Right() time.Sleep(3 * time.Second) r.Stop() time.Sleep(3 * time.Second) r.Start() time.Sleep(3 * time.Second)}// RoverDriver drives a rover around the surface of Mars.type RoverDriver struct { commandc chan command}// NewRoverDriver starts a new RoverDriver and returns it.func NewRoverDriver() *RoverDriver { r := &RoverDriver{ commandc: make(chan command), } go r.drive() return r}type command intconst ( right = command(0) left = command(1) start = command(2) stop = command(3))// drive is responsible for driving the rover. It// is expected to be started in a goroutine.func (r *RoverDriver) drive() { pos := image.Point{X: 0, Y: 0} direction := image.Point{X: 1, Y: 0} updateInterval := 250 * time.Millisecond nextMove := time.After(updateInterval) speed := 1 for { select { case c := <-r.commandc: switch c { case right: direction = image.Point{ X: -direction.Y, Y: direction.X, } case left: direction = image.Point{ X: direction.Y, Y: -direction.X, } case stop: speed = 0 case start: speed = 1 } log.Printf("new direction %v; speed %d", direction, speed) case <-nextMove: pos = pos.Add(direction.Mul(speed)) log.Printf("moved to %v", pos) nextMove = time.After(updateInterval) } }}// Left turns the rover left (90° counterclockwise).func (r *RoverDriver) Left() { r.commandc <- left}// Right turns the rover right (90° clockwise).func (r *RoverDriver) Right() { r.commandc <- right}// Stop halts the rover.func (r *RoverDriver) Stop() { r.commandc <- stop}// Start gets the rover moving.func (r *RoverDriver) Start() { r.commandc <- start}