互斥锁
/*
目的:通过大并发读写同一个map变量,来展示go的大并发能力;
实现:设定go map变量,互斥锁Mutex, 改100个读协程,10个写协程; atomic纪录读写总次数;
方法:sync.Mutex{}, map [int] int ;
*/
package main
import (
"fmt"
"math/rand"
"sync"
"sync/atomic"
"time"
)
func main() {
var state = make(map [int] int)
var mutex = & sync.Mutex{}
var readTimes uint64
var writeTimes uint64
var totalV uint64
for r := 0; r < 100; r++ {
go func() {
/*先从state中读取数值--加-解锁,然后加到总值里面,最后刷新readTImes,延迟 */
for {
k := rand.Intn(5)
mutex.Lock()
k = state[k]
mutex.Unlock()
atomic.AddUint64(& readTimes, 1)
atomic.AddUint64(& totalV, uint64(k))
time.Sleep(time.Millisecond)
}
}()
}
for w := 0; w < 10; w++ {
go func() {
for {
k := rand.Intn(5)
v := rand.Intn(100)
mutex.Lock()
state[k] = v
mutex.Unlock()
atomic.AddUint64(&writeTimes, 1)
time.Sleep(time.Millisecond)
}
}()
}
time.Sleep(time.Second)
fmt.Println("Read times is :", atomic.LoadUint64(& readTimes))
fmt.Println("Write times is ", atomic.LoadUint64(& writeTimes))
mutex.Lock()
fmt.Println("State value: ", state)
mutex.Unlock()
fmt.Println("Total values is :", atomic.LoadUint64(& totalV))
}
results
Read times is : 57818
Write times is 5781
State value: map[0:19 1:98 2:12 3:63 4:16]
Total values is : 2855694
状态协程
/*
目的:通过协程方法(通道间通信),实现协程间state或值共享;而非互斥锁;
作用:协程方法共享状态,比互斥锁复杂;但是在管理多个互斥锁容易出错,这时候,协程间通信很有优势;
方法:定义状态的管理协程,定义读结构体,写结构体(传递变量和通道 控制), 定义读写协程::
--关键在于读或写协程中,一遍给读写通道输入读写结构体; 同时等待状态协程修改读写通道中的结构体数值;实现类锁机制,简洁易于懂---
Go 共享内存的思想是,通过通信使每个数据仅被单个协程所拥有,即通过通信实现共享内存。
变量和函数: type name struct{}, make(chan type); atomic.addUint64, atomic.loadUint64;
select操作ch,只会干其一,不会同读同写state;
多并发写: 通过内置ch,保证独占写状态; (这个协程没写完,state协程不返回状态,-state阻塞;而通道会该协程。使得他等待;)
*/
package main
import (
"fmt"
"math/rand"
"sync/atomic"
"time"
)
type writeStru struct {
k int
v int
resp chan bool
}
type readStru struct {
k int
resp chan int
}
func main() {
writeCh := make(chan writeStru)
readCh := make(chan readStru)
var writeNum uint64
var readNum uint64
var sumOp uint64
/*state 协程, state 是其内部独占的量,控制唯一性读写的依据 */
go func() {
state := make(map[int] int)
for {//select只是一次性的,需要循环选择
select {
case write := <- writeCh:
state[write.k] = write.v
write.resp <- true //写完后,通过写入true响应通道write方式来让写协程走完;
case read := <- readCh:
v := state[read.k]
read.resp <- v //通过返回通道,激活被阻塞的读协程
}
}
}()
/*写协程 */
for i:= 0; i < 10; i ++ {
go func() {
for {
writeOp := writeStru {
k : rand.Intn(5),
v : rand.Intn(100),
resp : make(chan bool),
}
writeCh <- writeOp //将新建的写结构体,通道传递给state修改状态,resp没返回前阻塞
<- writeOp.resp //阻塞等待 state协程,修改完
atomic.AddUint64(& writeNum, 1)
time.Sleep(time.Millisecond)
}
}()
}
/*读协程*/
for i := 0; i < 100; i ++ {
go func() {
for {
readOp := readStru {
k : rand.Intn(5),
resp: make(chan int),
}
readCh <- readOp
val := <- readOp.resp //等待state协程读完,返回值再处理
atomic.AddUint64(& sumOp, uint64(val))
atomic.AddUint64(& readNum, 1)
time.Sleep(time.Millisecond)
}
}()
}
time.Sleep(time.Second)
fmt.Println("100个读协程1s总读取次数: ", atomic.LoadUint64(& readNum))
fmt.Println("100个写协程1s总写入次数: ", atomic.LoadUint64(& writeNum))
fmt.Println("100个写协程1s累加状态值: ", atomic.LoadUint64(& sumOp))
}
results
100个读协程1s总读取次数: 59901
100个写协程1s总写入次数: 6000
100个写协程1s累加状态值: 3017231
summary
- 互斥锁应用并发的通用概念,实现容易;var读写前后都mutex.lock .unlock即可;但是多锁的逻辑和问题定位很要命
- 状态协同,需要设计多个通道在状态协程间修改;需要设计通信通道,再每次读写协程-状态协程间阻塞、切换; 实现较为复杂,但是因为内合了go的通道设计、并发的思想“Go 共享内存的思想是,通过通信使每个数据仅被单个协程所拥有,即通过通信实现共享内存。 基于通道的方法与该思想完全一致! ”
- 通道是go比较核心的通信、内存共享理念和并发关键点
- uint64格式需要注意,go是静态强类型语言

本文探讨了Go语言中使用互斥锁(Mutex)管理和大并发读写map的场景,与利用状态协程和通道实现的并发通信机制进行对比,揭示了Go内存共享哲学和通道在简化并发管理中的优势。
841

被折叠的 条评论
为什么被折叠?



