并发编程
0 Go 中的并发
Goroutine 是由Go运行时管理的轻量级线程
go function(x, y)
package main
import (
"fmt"
"time"
)
func say(s string) {
for range [10]int{} {
time.Sleep(10 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
Channels 管道 知识回顾
package main
import "fmt"
func main() {
c := make(chan int, 10) // 缓冲管道 缓冲10个元素
c <- 1 // 将值存入管道
v := <-c // 取出值 先进先出
fmt.Println(v)
}
c := make(chan int)
go func() {
v := <-c // 当 channel 无缓存时 发送和接收 都会被阻塞 直到另一个goroutine在该channel上接收、发送一个值
fmt.Println(v)
}()
c <- 1
time.Sleep(10 * time.Millisecond)
1 并发时同时修改全局变量,容易引起资源竞争
1.1 如何检测:
使用 -race 编译 资源竞争时报错:
go build -race main.go
package main
import (
"fmt"
"sync"
"time"
)
var (
num = 0
)
func main() {
for i := 0; i < 1000; i++ {
go runtimes(10)
}
time.Sleep(time.Second)
}
func runtimes(times int) int {
num = num + 1
for i := 1; i <= times; i++ {
fmt.Printf("i = %d\n", times-i)
fmt.Println(num)
time.Sleep(time.Second)
}
return times
}
==================
WARNING: DATA RACE
Write at 0x0000005e1380 by goroutine 7:
i = 9
main.runtimes()
/root/project/golang/learn/main.go:19 +0x4a
main.main.func1()
/root/project/golang/learn/main.go:12 +0x30
Previous read at 0x0000005e1380 by goroutine 13:
3
main.runtimes()
/root/project/golang/learn/main.go:22 +0xd6
main.main.func1()
/root/project/golang/learn/main.go:12 +0x30
Goroutine 7 (running) created at:
main.main()
/root/project/golang/learn/main.go:12 +0x32
Goroutine 13 (running) created at:
main.main()
/root/project/golang/learn/main.go:12 +0x32
==================
1.2 如何避免
1.2.1 互斥锁
互斥锁只能同时有一个线程运行
var (
num = 0
lock sync.Mutex // 单词: Mutex 互斥
)
lock.Lock() 加锁
lock.Unlock() 解锁
func runtimes(times int) int {
for i := 1; i <= times; i++ {
lock.Lock()
num = num + 1
lock.Unlock()
fmt.Printf("i = %d\n", times-i)
fmt.Println(num)
}
return times
}
1.2.2 读写锁
当读操作大于写操作时 使用读写锁
读写锁(
sync.RWMutex
)允许多读单写,适合读多写少的场景,增加并发读的效率。互斥锁(sync.Mutex
)一次只允许一个goroutine访问资源,不分读写,适用于读写操作频繁交替的情况。
package main
import (
"fmt"
"sync"
"time"
)
var RWLock sync.RWMutex
var list = make([]int, 0)
func W() {
RWLock.Lock() // 获取写锁
list = append(list, 1)
RWLock.Unlock() // 释放写锁
}
func R() {
RWLock.RLock() // 获取读锁
fmt.Println(list)
RWLock.RUnlock() // 释放读锁
}
func main() {
go W()
time.Sleep(10 * time.Millisecond) // 给写操作一点时间
go R()
go W()
go W()
go R()
time.Sleep(100 * time.Millisecond) // 等待goroutines完成
R()
}
1.2.3 管道
var intChan chan int
func main() {
intChan = make(chan int, 10)
sleep(1)
fmt.Println(<-intChan)
}
func sleep(i int) {
time.Sleep(time.Second)
intChan <- i
}
评论