go并发编程
目录
go并发编程
1. 任务调度
1.1 sync .WaitGroup
package main
import (
"fmt"
"sync"
)
// Goroutine并发编程
func goRoutineDemo() {
fmt.Println("GoRoutineDemo...")
}
var (
wg sync.WaitGroup // 类比java:countDownLatch
)
func goRoutineWg(i int) {
defer wg.Done() // 任务数-1
fmt.Println(i, "goRoutineWg...")
}
func main() {
// 单个任务
go goRoutineDemo()
// 多个任务同时进行 使用sync.WaitGroup + go func 方式
for i := 0; i < 10; i++ {
wg.Add(1) // 任务数+1
go goRoutineWg(i)
}
wg.Wait() // 阻塞等待所有任务完成
fmt.Println("GoRoutineMain...")
}
runOut:
GoRoutineDemo...
9 goRoutineWg...
3 goRoutineWg...
4 goRoutineWg...
5 goRoutineWg...
6 goRoutineWg...
7 goRoutineWg...
8 goRoutineWg...
1 goRoutineWg...
0 goRoutineWg...
2 goRoutineWg...
GoRoutineMain...
1.2 sync.Once
package main
import (
"fmt"
"sync"
)
var (
so sync.Once
)
func load() {
fmt.Println("初始化完成...")
}
func initOper() {
//so.Do(load) // 只执行一次 多用于初始化操作
so.Do(func() {
load()
})
}
func main() {
initOper()
initOper()
}
runOut:
初始化完成...
1.3 sync.Cond
package main
import (
"fmt"
"sync"
"time"
)
/**
sync.Cond 表示的是条件变量,用来协调多个 goroutine之间的同步。当条件满足 通过Signal或者Broadcast方法唤醒挂起的任务。
Wait(): 挂起所有goroutine任务 直到条件满足;调用Wait前 优先调用L.Lock加锁。
Signal(): 通知一个等待的goroutine任务。
Broadcast():通知所有等待的goroutine任务。
*/
var (
ok bool // 任务标识
)
func producer(cond *sync.Cond) {
fmt.Println("producer start")
time.Sleep(time.Millisecond * 10) // 保证消费者任务挂起等待(加锁)
cond.L.Lock()
time.Sleep(time.Millisecond * 30)
ok = true
fmt.Println("producer end")
cond.L.Unlock()
cond.Broadcast() // 唤醒所有挂起的消费者任务
//cond.Signal() // 唤醒1个挂起的消费者任务(若任务耗时大于主程序等待耗时 会导致任务丢失)
fmt.Println("唤醒等待任务>>>")
}
func consumer(name string, cond *sync.Cond) {
fmt.Printf("consumer-%s start\n", name)
cond.L.Lock()
for !ok {
fmt.Printf("consumer-%s wait\n", name)
cond.Wait() // 挂起当前任务 直到调用Signal或Broadcast唤醒
}
fmt.Printf("consumer-%s work\n", name)
time.Sleep(time.Millisecond * 10) // 模拟消费者任务耗时
fmt.Printf("consumer-%s end\n", name)
defer cond.L.Unlock()
}
func main() {
cond := sync.NewCond(&sync.Mutex{}) // 实例化 Locker入参为sync.Mutex或者sync.RWMutex
taskLi := []string{"A", "B"} // 任务集
for _, v := range taskLi {
go consumer(v, cond)
}
go producer(cond)
time.Sleep(time.Second) // 等待任务结束
fmt.Println("程序执行结束!!!")
}
runOut:
producer start
consumer-A start
consumer-A wait
consumer-B start
consumer-B wait
producer end
唤醒等待任务>>>
consumer-A work
consumer-A end
consumer-B work
consumer-B end
程序执行结束!!!
1.4 runtime
package main
import (
"fmt"
"runtime"
"time"
)
// runtime
func countTask(count int) {
for i := 0; i < count; i++ {
if i == 2 {
defer fmt.Println("直接退出洗衣服任务")
runtime.Goexit() // 强制中止当前任务 不影响其它任务
}
fmt.Println("洗衣服:", i)
runtime.Gosched() // 当前任务挂起、其它任务执行
}
}
func cookTask(count int) {
for i := 0; i < count; i++ {
if i == 2 {
defer fmt.Println("直接退出做饭任务")
runtime.Goexit()
}
fmt.Println("做饭:", i)
runtime.Gosched() // 当前任务挂起、其它任务执行
}
}
func main() {
runtime.GOMAXPROCS(1) //CPU核心数
taskNum := 5 // 任务数
go countTask(taskNum)
go cookTask(taskNum)
time.Sleep(time.Second * 2) // 等待任务执行完毕
fmt.Println("所有任务结束...")
}
runOut:
做饭: 0
洗衣服: 0
做饭: 1
洗衣服: 1
直接退出做饭任务
直接退出洗衣服任务
所有任务结束...
1.5 channel
package main
import (
"fmt"
"time"
)
func taskB(ch <-chan int) {
fmt.Println("B start")
<-ch // 阻塞等待
fmt.Println("B end")
}
func taskA(ch chan int) {
defer close(ch)
fmt.Println("A start")
time.Sleep(time.Millisecond * 100) // 模拟耗时
fmt.Println("A end")
}
func main() {
/**
无缓冲管道: 需要接收和发送同时准备 且阻塞
有缓冲管道: 有缓冲空间则不阻塞
单向管道: 只发送或接收
*/
ch := make(chan int) // 通过管道协调任务调度顺序 并通过关闭通知所有等待的任务
go taskB(ch)
go taskB(ch)
go taskA(ch)
time.Sleep(time.Second) // 模拟等待任务结束
}
runOut:
A start
B start
B start
A end
B end
B end
2. 数据通信
2.1 channel
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 5) // 带缓冲通道
go func(ch chan int) {
for i := 0; i < 5; i++ {
fmt.Println("send:", i)
ch <- i
}
defer close(ch)
}(ch) // 发送
go func(ch chan int) {
for v := range ch {
fmt.Println("receiveA:", v)
}
}(ch) // 接收1
go func(ch chan int) {
for v := range ch {
fmt.Println("receiveB:", v)
}
}(ch) // 接收2
time.Sleep(time.Second) // 等待协程任务完成
}
runOut:
send: 0
send: 1
send: 2
send: 3
send: 4
receiveA: 1
receiveA: 2
receiveA: 3
receiveA: 4
receiveB: 0
2.2 channel + select
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan int, 1)
ch2 := make(chan int, 1)
// select 同时监听多个管道情况 且只处理未阻塞的 当多个同时未阻塞 则随机处理
go func(ch chan int) {
time.Sleep(time.Millisecond * 200)
ch <- 11
defer close(ch)
}(ch1)
go func(ch chan int) {
time.Sleep(time.Millisecond * 500)
ch <- 111
defer close(ch)
}(ch2)
for i := 0; i < 10; i++ {
select {
case d, ok := <-ch1:
if ok {
fmt.Println("ch1接收到数据:", d)
} else {
fmt.Println("ch1已关闭")
}
case d, ok := <-ch2:
if ok {
fmt.Println("ch2接收到数据:", d)
} else {
fmt.Println("ch2已关闭")
}
//case <-time.After(time.Millisecond * 600):
// fmt.Println("超时机制执行")
default:
fmt.Println("default执行")
}
time.Sleep(time.Millisecond * 200)
}
}
runOut:
default执行
default执行
ch1接收到数据: 11
ch1已关闭
ch2接收到数据: 111
ch1已关闭
ch2已关闭
ch2已关闭
ch2已关闭
ch1已关闭
2.3 sync.Mutex
package main
import (
"fmt"
"sync"
)
var (
sum = 0 // 全局变量
)
func main() {
lock := sync.Mutex{} // 锁(互斥锁、读写锁)
wg := sync.WaitGroup{}
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
lock.Lock() // 加锁
sum++
lock.Unlock() // 解锁
}()
}
wg.Wait() // 等待所有任务执行完成
fmt.Println("sum=", sum)
}
runOut:
sum=100
2.4 atomic
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var count int64 // 全局变量
func main() {
/**
sync.atomic 保证特定类型同一时刻的原子性操作
类型:int32|int64|uint32|uint64|uintptr|unsafe.Pointer
操作:
读取 eg:LoadInt32(addr int32)(val int32)
写入 eg:StoreInt32(addr *int32, val int32)
修改 eg:AddInt32(addr *int32, delta int32)(new int32)
交换 eg:SwapInt32(addr *in32, new int32)(old int32)
比较交换 eg:CompareAndSwapInt32(addr *32, old, new int32)(swapped bool)
*/
var wg = sync.WaitGroup{}
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
atomic.AddInt64(&count, 1) // 原子操作
}()
}
wg.Wait()
fmt.Println("count=", count)
}
runOut:
count=100
2.5 sync.Map
package main
import (
"fmt"
"sync"
"time"
)
var (
sMap sync.Map
iMap sync.Map
wig sync.WaitGroup
)
func main() {
/**
sync.Map 并发安全 读多写少 读无锁写加锁
操作:
读取:Load(key any)(value any, ok bool) 根据key获取value 存在返回value 不存在返回nil
写入:Store(key,value any): 新增或更新
查找写入:LoadOrStore(key,value any)(actual any, loaded bool): 若key存在则返回value, loaded为true;若key不存在则写入value,loaded为false
删除:Delete(key any): 删除key
获取并删除: LoadAndDelete(key any): 获取key对应的value值并删除key
比较删除: CompareAndDelete(key, old any) bool
比较交换: CompareAndSwap(key, old, new any) bool
交换: Swap(key, value any) (any, bool)
遍历: Range(f func(key,value any) bool): 遍历map key和val
*/
fmt.Println("操作-sync.Map")
key := "name"
val := "sync.Map"
sMap.Store(key, val)
fmt.Printf("map Store: key:%s->val:%s\n", key, val)
v, ok := sMap.Load(key)
fmt.Printf("map Load: key:%s->val:%s;ok:%t\n", key, v, ok)
newKey := "age"
newVal := "10"
v, ok = sMap.LoadOrStore(newKey, newVal)
fmt.Printf("map LoadOrStore: key:%s->val:%s;ok:%t\n", key, v, ok)
sMap.Delete(newKey)
fmt.Printf("map Delete: key:%s->val:%s\n", newKey, newVal)
v, ok = sMap.LoadAndDelete(key)
fmt.Printf("map LoadAndDelete: key:%s->val:%s\n", key, val)
for i := 0; i < 5; i++ {
sMap.Store(i, i)
}
sMap.Range(func(k, v any) bool {
fmt.Printf("map Range: key:%v->val:%v\n", k, v)
return true
})
fmt.Println("并发-sync.Map")
wig.Add(3)
go func() {
for i := 0; i < 3; i++ {
iMap.Store(i, i)
}
defer wig.Done()
}()
time.Sleep(time.Millisecond * 300) // 优先写完
go func() {
iMap.Range(func(k, v any) bool {
fmt.Println("readA:", k, v)
return true
})
defer wig.Done()
}()
go func() {
iMap.Range(func(k, v any) bool {
fmt.Println("readB:", k, v)
return true
})
defer wig.Done()
}()
wig.Wait()
}
runOut:
操作-sync.Map
map Load: key:name->val:sync.Map;ok:true
map LoadOrStore: key:name->val:10;ok:false
map Delete: key:age->val:10
map LoadAndDelete: key:name->val:sync.Map
map Range: key:0->val:0
map Range: key:1->val:1
map Range: key:2->val:2
map Range: key:3->val:3
map Range: key:4->val:4
并发-sync.Map
readB: 0 0
readB: 1 1
readB: 2 2
readA: 0 0
readA: 1 1
readA: 2 2
2.6 sync.Pool
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var (
created int32
wcg sync.WaitGroup
workerNum = 5
)
func main() {
/**
sync.Pool Get、Put并发安全 New不确定是否安全 通过New实例对象 Get从Pool获取对象 Put释放对象到Pool
临时对象缓存、增加临时对象的重用率、减少内存碎片
参考: https://zhuanlan.zhihu.com/p/591805870
*/
pool := &sync.Pool{New: func() any {
atomic.AddInt32(&created, 1)
buf := make([]byte, 16)
return &buf
}}
wcg.Add(workerNum)
for i := 0; i < workerNum; i++ {
go func() {
defer wcg.Done()
// 从pool获取一个临时对象
buf := pool.Get()
// pool回收一个临时对象
defer pool.Put(buf)
}()
}
wcg.Wait()
fmt.Println("次数=", created)
}
runOut:
次数= 3

浙公网安备 33010602011771号