并发状态
Go 的互斥锁(mutex)
- mutex = mutual exclusive
- Lock(),Unlock()
- sync 包
package main
import "sync"
var mu sync.Mutex
func main() {
mu.Lock()
defer mu.Unlock()
// The lock is held until we return from the function.
}
package main
import "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 main
import (
"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 main
import (
"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 main
import (
"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 int
const (
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 main
import (
"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 main
import (
"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 int
const (
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
}